#4941 #5049 #5050
This commit is contained in:
Nino Righi
2025-04-28 15:36:03 +00:00
parent 809a6e38b3
commit eba9cec16e
386 changed files with 6742 additions and 2591 deletions

View 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.

View File

@@ -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',
},
],

View File

@@ -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',

View File

@@ -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": {

View File

@@ -0,0 +1,2 @@
export * from './lib/errors';
export * from './lib/models';

View 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');
});
});
});

View 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);
}
}

View File

@@ -0,0 +1,3 @@
export * from './data-access.error';
export * from './property-is-empty.error';
export * from './property-is-null-or-undefined.error';

View File

@@ -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.`);
});
});

View File

@@ -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.`);
}
}

View File

@@ -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.`,
);
});
});

View File

@@ -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.`,
);
}
}

View File

@@ -0,0 +1,2 @@
export * from './errors';
export * from './models';

View 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;
}

View 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;

View 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;
}

View 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';

View 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;
}

View 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;
}

View File

@@ -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.

View File

@@ -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';

View File

@@ -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(),
});
}

View File

@@ -1,7 +0,0 @@
export interface EntityContainer<T> {
id: number;
displayName?: string;
data?: T;
enabled?: boolean;
selected?: boolean;
}

View File

@@ -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(),
});
}

View File

@@ -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;
}

View File

@@ -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;