mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
7
libs/common/data-access/README.md
Normal file
7
libs/common/data-access/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# common-data-access
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test common-data-access` to execute the unit tests.
|
||||
@@ -12,7 +12,7 @@ export default [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
prefix: 'common',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
@@ -20,7 +20,7 @@ export default [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
prefix: 'common',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
@@ -1,8 +1,8 @@
|
||||
export default {
|
||||
displayName: 'common-result',
|
||||
displayName: 'common-data-access',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/common/result',
|
||||
coverageDirectory: '../../../coverage/libs/common/data-access',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "common-result",
|
||||
"name": "common-data-access",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/common/result/src",
|
||||
"prefix": "lib",
|
||||
"sourceRoot": "libs/common/data-access/src",
|
||||
"prefix": "common",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
@@ -10,7 +10,7 @@
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/common/result/jest.config.ts"
|
||||
"jestConfig": "libs/common/data-access/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
2
libs/common/data-access/src/index.ts
Normal file
2
libs/common/data-access/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './lib/errors';
|
||||
export * from './lib/models';
|
||||
100
libs/common/data-access/src/lib/errors/data-access.error.spec.ts
Normal file
100
libs/common/data-access/src/lib/errors/data-access.error.spec.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { DataAccessError } from './data-access.error';
|
||||
|
||||
/**
|
||||
* Test suite for DataAccessError class.
|
||||
* Validates the functionality of the base error class used for structured error handling.
|
||||
*/
|
||||
describe('DataAccessError', () => {
|
||||
/**
|
||||
* Tests basic error functionality without additional data
|
||||
*/
|
||||
describe('Basic Error without Data', () => {
|
||||
class TestError extends DataAccessError<'TEST_ERROR'> {
|
||||
constructor(message: string) {
|
||||
super('TEST_ERROR', message, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
it('should create an error with a code and message', () => {
|
||||
// Arrange & Act
|
||||
const error = new TestError('Test message');
|
||||
|
||||
// Assert
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(DataAccessError);
|
||||
expect(error.code).toBe('TEST_ERROR');
|
||||
expect(error.message).toBe('Test message');
|
||||
expect(error.data).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests error functionality with additional data, ensuring type safety
|
||||
* and proper data handling
|
||||
*/
|
||||
describe('Error with Data', () => {
|
||||
interface ValidationData {
|
||||
field: string;
|
||||
issues: string[];
|
||||
}
|
||||
|
||||
class ValidationError extends DataAccessError<
|
||||
'VALIDATION_ERROR',
|
||||
ValidationData
|
||||
> {
|
||||
constructor(message: string, data: ValidationData) {
|
||||
super('VALIDATION_ERROR', message, data);
|
||||
}
|
||||
}
|
||||
|
||||
it('should create an error with code, message, and data', () => {
|
||||
// Arrange
|
||||
const validationData: ValidationData = {
|
||||
field: 'email',
|
||||
issues: ['Invalid format', 'Required field'],
|
||||
};
|
||||
|
||||
// Act
|
||||
const error = new ValidationError('Validation failed', validationData);
|
||||
|
||||
// Assert
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error).toBeInstanceOf(DataAccessError);
|
||||
expect(error.code).toBe('VALIDATION_ERROR');
|
||||
expect(error.message).toBe('Validation failed');
|
||||
expect(error.data).toEqual(validationData);
|
||||
});
|
||||
|
||||
it('should maintain data type safety', () => {
|
||||
// Arrange
|
||||
const validationData: ValidationData = {
|
||||
field: 'username',
|
||||
issues: ['Too short'],
|
||||
};
|
||||
|
||||
// Act
|
||||
const error = new ValidationError('Validation failed', validationData);
|
||||
|
||||
// Assert
|
||||
expect(error.data.field).toBe('username');
|
||||
expect(error.data.issues).toContain('Too short');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests type safety enforcement for error codes and data
|
||||
*/
|
||||
describe('Error Type Safety', () => {
|
||||
it('should ensure type safety for error codes', () => {
|
||||
class TypedError extends DataAccessError<'TYPE_ERROR'> {
|
||||
constructor() {
|
||||
super('TYPE_ERROR', 'Type error message', undefined);
|
||||
}
|
||||
}
|
||||
|
||||
const error = new TypedError();
|
||||
const errorCode: 'TYPE_ERROR' = error.code; // TypeScript will error if type safety fails
|
||||
expect(errorCode).toBe('TYPE_ERROR');
|
||||
});
|
||||
});
|
||||
});
|
||||
48
libs/common/data-access/src/lib/errors/data-access.error.ts
Normal file
48
libs/common/data-access/src/lib/errors/data-access.error.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Base error class for data access related errors. This class provides structured error
|
||||
* handling with type-safe error codes and optional additional data.
|
||||
*
|
||||
* Features:
|
||||
* - Type-safe error codes using string literals
|
||||
* - Optional additional error data with type safety
|
||||
* - Standardized error message format
|
||||
*
|
||||
* @template TCode - String literal type for the error code. Enforces type safety for error codes.
|
||||
* @template TData - Type for additional error data (defaults to void). Allows attaching context-specific data.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Define an error with a specific code and no data
|
||||
* class MyError extends DataAccessError<'MY_ERROR'> {
|
||||
* constructor(message: string) {
|
||||
* super('MY_ERROR', message);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Define an error with data
|
||||
* class ValidationError extends DataAccessError<'VALIDATION_ERROR', string[]> {
|
||||
* constructor(errors: string[]) {
|
||||
* super('VALIDATION_ERROR', 'Validation failed', errors);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see {@link PropertyIsEmptyError} for example implementation with no data
|
||||
* @see {@link CreateReturnProcessError} for example implementation with data
|
||||
*/
|
||||
export class DataAccessError<TCode extends string, TData = void> extends Error {
|
||||
/**
|
||||
* Creates a new DataAccessError instance.
|
||||
*
|
||||
* @param code - The error code that identifies the type of error. Must match the TCode type parameter.
|
||||
* @param message - A human-readable description of the error.
|
||||
* @param data - Additional data related to the error. Must match the TData type parameter.
|
||||
*/
|
||||
constructor(
|
||||
public readonly code: TCode,
|
||||
message: string,
|
||||
public readonly data: TData,
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
3
libs/common/data-access/src/lib/errors/index.ts
Normal file
3
libs/common/data-access/src/lib/errors/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './data-access.error';
|
||||
export * from './property-is-empty.error';
|
||||
export * from './property-is-null-or-undefined.error';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { DataAccessError } from './data-access.error';
|
||||
import { PropertyIsEmptyError } from './property-is-empty.error';
|
||||
|
||||
describe('PropertyIsEmptyError', () => {
|
||||
it('should create an error with the correct code and message', () => {
|
||||
// Arrange
|
||||
const propertyName = 'username';
|
||||
|
||||
// Act
|
||||
const error = new PropertyIsEmptyError(propertyName);
|
||||
|
||||
// Assert
|
||||
expect(error).toBeInstanceOf(PropertyIsEmptyError);
|
||||
expect(error).toBeInstanceOf(DataAccessError);
|
||||
expect(error.code).toBe('PROPERTY_IS_EMPTY');
|
||||
expect(error.message).toBe(`Property "${propertyName}" is empty.`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { DataAccessError } from './data-access.error';
|
||||
|
||||
/**
|
||||
* Represents an error that is thrown when a required property is empty.
|
||||
*
|
||||
* @remarks
|
||||
* This error is identified by the code 'PROPERTY_IS_EMPTY'.
|
||||
*
|
||||
* @param propertyName - The name of the property that is empty.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* throw new PropertyIsEmptyError("username");
|
||||
* ```
|
||||
*/
|
||||
export class PropertyIsEmptyError extends DataAccessError<'PROPERTY_IS_EMPTY'> {
|
||||
constructor(propertyName: string) {
|
||||
super('PROPERTY_IS_EMPTY', `Property "${propertyName}" is empty.`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { PropertyNullOrUndefinedError } from './property-is-null-or-undefined.error';
|
||||
|
||||
describe('PropertyNullOrUndefinedError', () => {
|
||||
it('should create an error instance with the correct code and message', () => {
|
||||
// Arrange
|
||||
const propertyName = 'userId';
|
||||
|
||||
// Act
|
||||
const error = new PropertyNullOrUndefinedError(propertyName);
|
||||
|
||||
// Assert
|
||||
expect(error).toBeInstanceOf(PropertyNullOrUndefinedError);
|
||||
expect(error.code).toBe('PROPERTY_NULL_OR_UNDEFINED');
|
||||
expect(error.message).toBe(
|
||||
`Property "${propertyName}" is null or undefined.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { DataAccessError } from './data-access.error';
|
||||
|
||||
/**
|
||||
* Error thrown when a required property is null or undefined.
|
||||
*
|
||||
* @remarks
|
||||
* This error indicates that a non-null, non-undefined property has been found to be invalid.
|
||||
*
|
||||
* @param parameterName - The name of the property that is null or undefined.
|
||||
*
|
||||
* @example
|
||||
* throw new PropertyNullOrUndefinedError('userId');
|
||||
*/
|
||||
export class PropertyNullOrUndefinedError extends DataAccessError<'PROPERTY_NULL_OR_UNDEFINED'> {
|
||||
constructor(propertyName: string) {
|
||||
super(
|
||||
'PROPERTY_NULL_OR_UNDEFINED',
|
||||
`Property "${propertyName}" is null or undefined.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
2
libs/common/data-access/src/lib/index.ts
Normal file
2
libs/common/data-access/src/lib/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './errors';
|
||||
export * from './models';
|
||||
38
libs/common/data-access/src/lib/models/async-result.ts
Normal file
38
libs/common/data-access/src/lib/models/async-result.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Constant object defining the possible states of an asynchronous operation.
|
||||
* Used as a type-safe alternative to string literals for status values.
|
||||
*/
|
||||
export const AsyncResultStatus = {
|
||||
Idle: 'Idle',
|
||||
Pending: 'Pending',
|
||||
Success: 'Success',
|
||||
Error: 'Error',
|
||||
} as const;
|
||||
|
||||
export type AsyncResultStatus =
|
||||
(typeof AsyncResultStatus)[keyof typeof AsyncResultStatus];
|
||||
|
||||
/**
|
||||
* Generic interface representing the state of an asynchronous operation.
|
||||
* Combines status tracking with optional data and error information.
|
||||
*
|
||||
* @template T - The type of data returned on successful operations
|
||||
*/
|
||||
export interface AsyncResult<T> {
|
||||
/**
|
||||
* Current status of the asynchronous operation
|
||||
*/
|
||||
status: AsyncResultStatus;
|
||||
|
||||
/**
|
||||
* Data returned by a successful operation
|
||||
* Only defined when status is Success
|
||||
*/
|
||||
data?: T;
|
||||
|
||||
/**
|
||||
* Error information from a failed operation
|
||||
* Only defined when status is Error
|
||||
*/
|
||||
error?: unknown;
|
||||
}
|
||||
17
libs/common/data-access/src/lib/models/callback-result.ts
Normal file
17
libs/common/data-access/src/lib/models/callback-result.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Represents the result of an asynchronous operation that may succeed or fail.
|
||||
* Either contains the successful data or an error, but not both.
|
||||
*
|
||||
* @template T - The type of data returned on successful operations
|
||||
*/
|
||||
export type CallbackResult<T> =
|
||||
| { data: T; error?: unknown }
|
||||
| { data?: T; error: unknown };
|
||||
|
||||
/**
|
||||
* Function type for callbacks that receive operation results.
|
||||
* Typically used for handling asynchronous operation completions.
|
||||
*
|
||||
* @template T - The type of data returned on successful operations
|
||||
*/
|
||||
export type Callback<T> = (result: CallbackResult<T>) => void;
|
||||
34
libs/common/data-access/src/lib/models/entity-cotnainer.ts
Normal file
34
libs/common/data-access/src/lib/models/entity-cotnainer.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Generic container for entity objects that provides standard properties for display and selection state.
|
||||
* Used for consistent entity representation in lists, dropdowns, and selection components.
|
||||
*
|
||||
* @template T - The type of data contained within the entity container
|
||||
*/
|
||||
export interface EntityContainer<T> {
|
||||
/**
|
||||
* Unique identifier for the entity
|
||||
*/
|
||||
id: number;
|
||||
|
||||
/**
|
||||
* Human-readable name for display in UI elements
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* The actual entity data object
|
||||
*/
|
||||
data?: T;
|
||||
|
||||
/**
|
||||
* Whether the entity is enabled/available for interaction
|
||||
* When false, the entity might be shown as disabled in UI
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the entity is currently selected
|
||||
* Useful for multi-select interfaces
|
||||
*/
|
||||
selected?: boolean;
|
||||
}
|
||||
5
libs/common/data-access/src/lib/models/index.ts
Normal file
5
libs/common/data-access/src/lib/models/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './async-result';
|
||||
export * from './callback-result';
|
||||
export * from './entity-cotnainer';
|
||||
export * from './list-response-args';
|
||||
export * from './response-args';
|
||||
27
libs/common/data-access/src/lib/models/list-response-args.ts
Normal file
27
libs/common/data-access/src/lib/models/list-response-args.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ResponseArgs } from './response-args';
|
||||
|
||||
/**
|
||||
* Extended response structure specifically for paginated list data.
|
||||
* Includes pagination metadata in addition to standard response properties.
|
||||
*
|
||||
* @template T - The type of individual items in the result array
|
||||
*/
|
||||
export interface ListResponseArgs<T> extends ResponseArgs<T[]> {
|
||||
/**
|
||||
* Number of items skipped in the result set (pagination offset)
|
||||
* Used for calculating the starting position in paginated results
|
||||
*/
|
||||
skip: number;
|
||||
|
||||
/**
|
||||
* Maximum number of items to include in the result set (page size)
|
||||
* Defines how many items should be returned per page
|
||||
*/
|
||||
take: number;
|
||||
|
||||
/**
|
||||
* Total number of items available in the complete result set
|
||||
* Used for calculating total pages and showing pagination information
|
||||
*/
|
||||
hits: number;
|
||||
}
|
||||
30
libs/common/data-access/src/lib/models/response-args.ts
Normal file
30
libs/common/data-access/src/lib/models/response-args.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Standard response structure for API requests returning data of type T.
|
||||
* Provides consistent error handling and result access across the application.
|
||||
*
|
||||
* @template T - The type of data contained in the result property
|
||||
*/
|
||||
export interface ResponseArgs<T> {
|
||||
/**
|
||||
* Indicates whether the request resulted in an error
|
||||
* When true, the result may be undefined or contain partial data
|
||||
*/
|
||||
error: boolean;
|
||||
|
||||
/**
|
||||
* Map of property names to error messages for validation failures
|
||||
* Keys represent property names, values contain validation error messages
|
||||
*/
|
||||
invalidProperties: Record<string, string>;
|
||||
|
||||
/**
|
||||
* Optional message providing additional information about the response
|
||||
* Typically used for error descriptions or success confirmations
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* The actual data returned by the request
|
||||
*/
|
||||
result: T;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# common-result
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test common-result` to execute the unit tests.
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from './lib/response-args.schema';
|
||||
export * from './lib/response-args';
|
||||
export * from './lib/entity-cotnainer';
|
||||
export * from './lib/entity-container.schema';
|
||||
export * from './lib/result';
|
||||
@@ -1,15 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const _EntityContainerSchema = z.object({
|
||||
id: z.number(),
|
||||
uId: z.string().optional(),
|
||||
displayLabel: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
selected: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export function EntityContainerSchema<T>(data: z.ZodType<T>) {
|
||||
return _EntityContainerSchema.extend({
|
||||
data: data.optional(),
|
||||
});
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface EntityContainer<T> {
|
||||
id: number;
|
||||
displayName?: string;
|
||||
data?: T;
|
||||
enabled?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// @deprecated
|
||||
export const ResponseArgsSchema = z.object({
|
||||
error: z.boolean(),
|
||||
invalidProperties: z.record(z.string()),
|
||||
message: z.string().optional(),
|
||||
});
|
||||
|
||||
// @deprecated
|
||||
export function CreateResponseArgs<T>(resultSchema: z.ZodType<T>) {
|
||||
return ResponseArgsSchema.extend({
|
||||
result: resultSchema,
|
||||
});
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
const ListResponseArgsSchema = ResponseArgsSchema.extend({
|
||||
skip: z.number(),
|
||||
take: z.number(),
|
||||
hits: z.number(),
|
||||
});
|
||||
|
||||
// @deprecated
|
||||
export function CreateListResponseArgs<T>(resultSchema: z.ZodType<T>) {
|
||||
return ListResponseArgsSchema.extend({
|
||||
result: resultSchema.array(),
|
||||
});
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
export interface ResponseArgs<T> {
|
||||
error: boolean;
|
||||
invalidProperties: Record<string, string>;
|
||||
message?: string;
|
||||
result: T;
|
||||
}
|
||||
|
||||
export interface ListResponseArgs<T> extends ResponseArgs<T[]> {
|
||||
skip: number;
|
||||
take: number;
|
||||
hits: number;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
export const ResultStatus = {
|
||||
Idle: 'idle',
|
||||
Pending: 'pending',
|
||||
Success: 'success',
|
||||
Error: 'error',
|
||||
} as const;
|
||||
|
||||
export type ResultStatus = (typeof ResultStatus)[keyof typeof ResultStatus];
|
||||
|
||||
export interface Result<T> {
|
||||
status: ResultStatus;
|
||||
data: T;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export type CallbackResult<T> = { data: T; error?: unknown } | { data?: T; error: unknown };
|
||||
|
||||
export type Callback<T> = (result: CallbackResult<T>) => void;
|
||||
Reference in New Issue
Block a user