@getanthill/datastore
Version:
Event-Sourced Datastore
417 lines (378 loc) • 9.62 kB
text/typescript
import type { FindCursor, Db } from 'mongodb';
import type { NextFunction, Request, Response, Router } from 'express';
import type { EventEmitter } from 'events';
import type { Datastore } from '../sdk';
import type { MongoDbConnector } from '@getanthill/mongodb-connector';
import type Ajv from 'ajv';
import type { Models } from '../models';
import type MQTTClient from '../services/mqtt';
import type AMQPClient from '../services/amqp';
import type FullyHomomorphicEncryptionClient from '../services/fhe';
import type Authz from '../services/authz';
import type { envConfig } from '../config';
import * as metrics from '../constants/metrics';
import * as telemetry from '@getanthill/telemetry';
export * as authorizations from './authorizations';
export type Source = 'entities' | 'events';
export type AccessLevel = 'read' | 'decrypt' | 'write' | 'admin';
export interface Access {
id: string;
token: string;
level: AccessLevel;
}
export interface Event {
type: string;
v?: string;
[x: string]: any;
}
export interface ResponseError extends Error {
name: string;
status?: number;
details?: object[];
}
export type Telemetry = typeof telemetry;
export type Reducer = (state: object, event: Event, validator?: Ajv) => object;
export type DatastoreConfig = typeof envConfig;
export type AnyObject = { [key: string]: any };
export type Any = null | number | string | boolean | AnyObject | AnyObject[];
export interface Services {
config: DatastoreConfig;
telemetry: Telemetry;
metrics: typeof metrics;
signals: EventEmitter;
api: (services: Services) => Promise<Router>;
events: (services: Services) => Promise<void>;
mongodb: MongoDbConnector;
mqtt: MQTTClient;
amqp: AMQPClient;
fhe: FullyHomomorphicEncryptionClient;
models: Models;
authz: Authz;
datastores: Map<string, Datastore>;
}
export interface ApiErrorsObject {
message?: {
[key: string]:
| number
| ((
err: Error,
req: Request,
res: Response,
next: NextFunction,
) => Promise<number>);
};
code?: {
[key: string]:
| number
| ((
err: Error,
req: Request,
res: Response,
next: NextFunction,
) => Promise<number>);
};
}
export type ProcessDestroySignal =
| 'SIGTERM'
| 'SIGINT'
| 'uncaughtException'
| 'unhandledRejection';
export interface ModelSchema {
model?: any;
events?: any;
}
export interface ModelIndex {
collection: string;
fields: object | { [key: string]: string | number };
opts: {
name?: string;
[key: string]: any;
};
}
export interface Processing {
name: string;
field: string;
purpose: string;
persons: string[];
recipients: string[];
duration_in_seconds: number;
tokens?: string[];
security_policies?: string;
owner?: {
name: string;
email?: string;
phone?: string;
url?: string;
location?: {
address?: string;
post_code?: string;
city?: string;
country_code?: string;
region?: string;
};
};
}
export interface ModelConfig {
name: string;
correlation_field: string;
schema: ModelSchema;
// ---
db?: string;
is_enabled?: boolean;
description?: string;
retry_duration?: number;
indexes?: ModelIndex[];
encrypted_fields?: string[];
links?: {
[key: string]: string;
};
// Model options:
with_default_events?: boolean;
with_global_version?: boolean;
// Fully Homomorphic logic
with_fully_homomorphic_encryption?: boolean;
fhe_public_key_field?: string;
// Blockchain logic
with_blockchain_hash?: boolean;
current_hash_field?: string;
previous_hash_field?: string;
nonce_field?: string;
blockchain_hash_difficulty?: number;
blockchain_hash_genesis?: string;
// Performance options:
must_wait_state_persistence?: boolean;
[key: string]: any;
// Processings
processings?: Processing[];
}
export interface DatastoreImportLink {
model: string;
id?: string;
is_idempotency_condition?: boolean;
idempotency?: {
[key: string]: any;
};
map:
| string
| {
[key: string]: string;
};
}
export interface DatastoreImportFixture {
model: string;
id: string;
idempotency: {
[key: string]: any;
};
omit_on_update?: string[];
links?: DatastoreImportLink[];
entity: any;
}
export interface HandlerFunc {
(
obj: any,
metadata: {
handlerId?: string;
path?: string;
datastore?: string;
model?: string;
source?: Source;
raw?: boolean;
},
): Promise<any>;
}
export interface StartFunc {
(): Promise<RunnerServices>;
}
export interface StopFunc {
(): Promise<void>;
}
export interface RunnerServices {
telemetry?: Telemetry;
datastores: {
[key: string]: Datastore;
};
[key: string]: any;
}
export interface RunnerTrigger {
datastore?: string;
model?: string;
source?: Source;
raw?: boolean;
query?: object;
headers?: object;
queryAsJSONSchema?: boolean;
processing?: {
correlation_field: string;
field: string;
states: [string, string, string?, string?];
};
}
export interface HandlerConfig extends RunnerTrigger {
triggers?: RunnerTrigger[];
start: StartFunc;
stop: StopFunc;
handler: HandlerFunc;
}
export interface HandlerBuilderFunc {
(url?: URL): Promise<HandlerConfig>;
}
export type EntityState = any;
export type ModelConstructor<T> = new (
services: Services,
correlationId?: string,
retryDuration?: number,
) => T;
export interface HandleOptions {
mustEncrypt?: boolean;
imperativeVersion?: number;
isReadonly?: string;
mustWaitStatePersistence?: boolean;
retryDuration?: number;
}
export interface ModelInstance {
state: EntityState;
mongodb: MongoDbConnector;
retryDuration: number;
correlationId: string;
latestHandledEvents: Event[];
handleWithRetry(
handler: () => Event[],
retryDuration: number,
storeStateErrorHandler: (...args: any) => void,
handleOptions?: HandleOptions,
): Promise<any>;
apply(
eventType: string,
data: AnyObject,
handleOptions?: HandleOptions,
v?: string,
): Promise<ModelInstance>;
getState(): Promise<EntityState>;
patch(data: AnyObject, handleOptions?: HandleOptions): Promise<ModelInstance>;
update(
data: AnyObject,
handleOptions?: HandleOptions,
): Promise<ModelInstance>;
create(
data: AnyObject,
handleOptions?: HandleOptions,
): Promise<ModelInstance>;
upsert(
data: AnyObject,
handleOptions?: HandleOptions,
): Promise<ModelInstance>;
getEvents(version?: number): FindCursor<any>;
restore(version: number): Promise<ModelInstance>;
rollback(events: Event[], updatedState: EntityState): Promise<ModelInstance>;
createSnapshot(): Promise<EntityState>;
getStateAtVersion(version: number, mustThrow?: boolean): Promise<EntityState>;
getNextVersion(): Promise<number>;
archive(): Promise<ModelInstance>;
unarchive(): Promise<ModelInstance>;
delete(): Promise<void>;
}
export interface ModelStatic {
IV_LENGTH: number;
RETRY_ERRORS: string[];
encryptionKeys: any[] | null;
hashedEncryptionKeys: any[] | null;
getSchema(): ModelSchema;
getModelConfig(): ModelConfig;
getOriginalSchema(): any;
getCorrelationField(): string;
getCollectionName(): string;
db(mongodb: MongoDbConnector, isRead?: boolean): Db;
explain(cursor: any, query: any): void;
count(mongodb: MongoDbConnector, query: any): Promise<number>;
find(mongodb: MongoDbConnector, query: any, opts?: any): FindCursor<any>;
isEncryptedField(field: string): boolean;
getStatesCollection(db: any): any;
getEventsCollection(db: any): any;
getSnapshotsCollection(db: any): any;
getEncryptionKeys(): string[];
getHashesEncryptionKeys(): string[];
getEligibleKeys(keys: string[]): string[];
getRequiredIndexes(): {
[x: string]: (
| object
| {
version: number;
name: string;
unique?: boolean;
}
)[][];
};
hashValue(data: any): string;
encrypt(
data: any,
additionalEncryptedFields?: string[],
keys?: string[],
hashes?: string[],
algorithm?: string,
): any;
decrypt(
data: any,
additionalEncryptedFields?: string[],
keys?: string[],
hashedKeys?: string[],
): any;
getIsReadonlyProperty(modelConfig?: ModelConfig): any;
getIsArchivedProperty(modelConfig?: ModelConfig): any;
getIsDeletedProperty(modelConfig?: ModelConfig): any;
}
export type GenericType = ModelConstructor<ModelInstance> & ModelStatic;
export interface Links {
[key: string]: { path: string; model: GenericType };
}
export interface BuiltLinks {
[key: string]: { operationId: string; parameters: { [key: string]: string } };
}
export interface SpecFragment {
openapi: string;
servers: Array<{
description: 'API Server';
url: '/api';
[key: string]: any;
}>;
info: {
title: string;
description: string;
version: string;
license: {
name: string;
url: string;
[key: string]: any;
};
[key: string]: any;
};
tags: Array<{
name: string;
description: string;
}>;
components: any;
paths: any;
}
export interface Ctx {
telemetry: Telemetry;
entities?: Map<string, any>;
projections?: any[];
data?: {
config: {
datastores: {
[key: string]: any;
};
imports?: {
date: any;
};
runner_params: any;
exit_timeout: any;
};
events: any[];
imports: any;
projections: any[];
assertions: any[];
};
instances?: any;
[key: string]: any;
}