@serialized/serialized-client
Advanced tools
Comparing version 8.2.0 to 8.3.0
import { BaseClient, StateBuilder } from './'; | ||
import { RetryStrategy } from "./RetryStrategy"; | ||
type AggregateType = string; | ||
type AggregateData = object; | ||
type EventType = string; | ||
type EventData = object; | ||
type AggregateId = string; | ||
type TenantId = string; | ||
export type DomainEvent<T extends AggregateType, D extends AggregateData> = { | ||
export type DomainEvent<T extends EventType, D extends EventData> = { | ||
readonly eventType: T; | ||
@@ -13,3 +13,3 @@ readonly data?: D; | ||
}; | ||
export type AggregatesClientConfig<T extends AggregateType> = { | ||
export type AggregatesClientConfig<T extends EventType> = { | ||
readonly aggregateType: T; | ||
@@ -16,0 +16,0 @@ readonly retryStrategy?: RetryStrategy; |
@@ -38,2 +38,17 @@ import { AxiosError } from "axios"; | ||
/** | ||
* Thrown if an error occurs in the client | ||
*/ | ||
export declare class ClientError extends SerializedError { | ||
message: string; | ||
cause?: Error; | ||
constructor(message: string, cause?: Error); | ||
} | ||
/** | ||
* Throw if an event is loaded that has no handler registered and no default handler is registered. | ||
*/ | ||
export declare class UnhandledEventTypeError extends ClientError { | ||
readonly eventType: string; | ||
constructor(eventType: string); | ||
} | ||
/** | ||
* Thrown if the client sent a request with an invalid payload. | ||
@@ -40,0 +55,0 @@ */ |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ServiceUnavailable = exports.isServiceUnavailable = exports.RateLimitExceeded = exports.isRateLimitExceeded = exports.UnauthorizedError = exports.isUnauthorizedError = exports.Conflict = exports.isConflict = exports.ConfigurationError = exports.isConfigurationError = exports.StateLoadingError = exports.isStateLoadingError = exports.AggregateNotFound = exports.isAggregateNotFound = exports.ProjectionNotFound = exports.isProjectionNotFound = exports.ProjectionDefinitionNotFound = exports.isProjectionDefinitionNotFound = exports.isInvalidPayload = exports.InvalidPayloadError = exports.UnexpectedClientError = exports.isUnexpectedClientError = exports.SerializedApiError = exports.isSerializedApiError = exports.SerializedError = exports.isSerializedError = void 0; | ||
exports.ServiceUnavailable = exports.isServiceUnavailable = exports.RateLimitExceeded = exports.isRateLimitExceeded = exports.UnauthorizedError = exports.isUnauthorizedError = exports.Conflict = exports.isConflict = exports.ConfigurationError = exports.isConfigurationError = exports.StateLoadingError = exports.isStateLoadingError = exports.AggregateNotFound = exports.isAggregateNotFound = exports.ProjectionNotFound = exports.isProjectionNotFound = exports.ProjectionDefinitionNotFound = exports.isProjectionDefinitionNotFound = exports.isInvalidPayload = exports.InvalidPayloadError = exports.UnhandledEventTypeError = exports.ClientError = exports.UnexpectedClientError = exports.isUnexpectedClientError = exports.SerializedApiError = exports.isSerializedApiError = exports.SerializedError = exports.isSerializedError = void 0; | ||
/** | ||
@@ -59,2 +59,25 @@ * Type guard to check if the thrown error is a SerializedError | ||
/** | ||
* Thrown if an error occurs in the client | ||
*/ | ||
class ClientError extends SerializedError { | ||
constructor(message, cause) { | ||
super(message); | ||
this.message = message; | ||
this.cause = cause; | ||
this.name = 'ClientError'; | ||
} | ||
} | ||
exports.ClientError = ClientError; | ||
/** | ||
* Throw if an event is loaded that has no handler registered and no default handler is registered. | ||
*/ | ||
class UnhandledEventTypeError extends ClientError { | ||
constructor(eventType) { | ||
super(`No event handler found for event type ${eventType}.`); | ||
this.eventType = eventType; | ||
this.name = 'UnhandledEventTypeError'; | ||
} | ||
} | ||
exports.UnhandledEventTypeError = UnhandledEventTypeError; | ||
/** | ||
* Thrown if the client sent a request with an invalid payload. | ||
@@ -61,0 +84,0 @@ */ |
@@ -8,3 +8,3 @@ import { BaseClient } from "./"; | ||
} | ||
export type GetSingleProjectionResponse = { | ||
export type ProjectionInstance = { | ||
readonly projectionId: string; | ||
@@ -71,3 +71,3 @@ readonly createdAt: number; | ||
export type ListSingleProjectionsResponse = { | ||
readonly projections: GetSingleProjectionResponse[]; | ||
readonly projections: ProjectionInstance[]; | ||
readonly hasMore: boolean; | ||
@@ -107,6 +107,7 @@ readonly totalCount: number; | ||
getProjectionDefinition(request: GetProjectionDefinitionRequest): Promise<LoadProjectionDefinitionResponse>; | ||
getSingleProjection(request: GetSingleProjectionRequest): Promise<GetSingleProjectionResponse>; | ||
getSingleProjection(request: GetSingleProjectionRequest): Promise<ProjectionInstance>; | ||
getAggregatedProjection(request: GetAggregatedProjectionRequest): Promise<GetAggregatedProjectionResponse>; | ||
delete(request: DeleteProjectionsRequest): Promise<void>; | ||
listSingleProjections(request: ListSingleProjectionRequest): Promise<ListSingleProjectionsResponse>; | ||
listAllProjections(request: ListSingleProjectionRequest): Promise<ListSingleProjectionsResponse>; | ||
countSingleProjections(request: CountSingleProjectionRequest): Promise<number>; | ||
@@ -113,0 +114,0 @@ private handleApiError; |
@@ -162,2 +162,25 @@ "use strict"; | ||
} | ||
listAllProjections(request) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let allPagesRead = false; | ||
let response = { | ||
hasMore: false, | ||
projections: [], | ||
totalCount: 0 | ||
}; | ||
let currentSkip = 0; | ||
const requestLimit = 1000; | ||
do { | ||
const currentPage = yield this.listSingleProjections(Object.assign(Object.assign({}, request), { skip: currentSkip, limit: requestLimit })); | ||
allPagesRead = !currentPage.hasMore; | ||
response = { | ||
hasMore: currentPage.hasMore, | ||
totalCount: response.totalCount + currentPage.totalCount, | ||
projections: [...response.projections, ...currentPage.projections] | ||
}; | ||
currentSkip += requestLimit; | ||
} while (!allPagesRead); | ||
return response; | ||
}); | ||
} | ||
countSingleProjections(request) { | ||
@@ -164,0 +187,0 @@ return __awaiter(this, void 0, void 0, function* () { |
@@ -0,11 +1,33 @@ | ||
import { DomainEvent } from "./AggregatesClient"; | ||
type EventHandlers<S, Events extends { | ||
eventType: string; | ||
}> = { | ||
[E in Events as `apply${E["eventType"]}`]: (currentState: S, event: E) => S; | ||
[E in Events as `apply${E["eventType"]}`]?: (currentState: S, event: E) => S; | ||
}; | ||
/** | ||
* Builder of aggregate state. | ||
* | ||
* Implements apply methods for all events that are relevant to the aggregate. | ||
* | ||
* This type also contains methods for a fallback default handler and initial state for the aggregate. | ||
*/ | ||
type StateBuilder<S, Events extends { | ||
eventType: string; | ||
}> = { | ||
/** | ||
* Initial state of the aggregate. | ||
* | ||
* Should return a map of the initial state of the aggregate (e.g. {orderId: null, status: 'UNDEFINED'}). | ||
*/ | ||
initialState: () => S; | ||
/** | ||
* Default handler for events that are not handled by the state builder. | ||
* | ||
* Typically this is used to handle events that are no longer relevant to the aggregate but are still stored in the event store. | ||
* If this is not provided, the state builder will throw an error when an unsupported event is loaded. | ||
* | ||
* @param e the loaded event | ||
*/ | ||
defaultHandler?: (currentState: S, e: DomainEvent<string, any>) => S; | ||
} & EventHandlers<S, Events>; | ||
export { StateBuilder }; |
import { StateBuilder } from "./StateBuilder"; | ||
/** | ||
* State builder for an aggregate. | ||
* | ||
* Loads the current state of an aggregate based on from the events that have been stored in the aggregate. | ||
*/ | ||
export declare class StateLoader<S, E extends { | ||
@@ -3,0 +8,0 @@ eventType: string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.StateLoader = void 0; | ||
const error_1 = require("./error"); | ||
/** | ||
* State builder for an aggregate. | ||
* | ||
* Loads the current state of an aggregate based on from the events that have been stored in the aggregate. | ||
*/ | ||
class StateLoader { | ||
loadState(currentState, stateBuilder, events) { | ||
events.forEach(e => { | ||
let eventType = e.eventType; | ||
currentState = stateBuilder[`apply${eventType}`](currentState, e); | ||
const eventType = e.eventType; | ||
const eventHandler = stateBuilder[`apply${eventType}`]; | ||
if (!eventHandler) { | ||
if (stateBuilder.defaultHandler) { | ||
stateBuilder.defaultHandler(currentState, e); | ||
} | ||
else { | ||
throw new error_1.UnhandledEventTypeError(eventType); | ||
} | ||
} | ||
else { | ||
currentState = eventHandler(currentState, e); | ||
} | ||
}); | ||
@@ -10,0 +27,0 @@ return currentState; |
@@ -6,3 +6,3 @@ { | ||
"author": "Serialized", | ||
"version": "8.2.0", | ||
"version": "8.3.0", | ||
"main": "dist/index.js", | ||
@@ -9,0 +9,0 @@ "types": "dist/index.d.ts", |
81073
1917