@getanthill/datastore
Version:
Event-Sourced Datastore
1,083 lines (1,075 loc) • 41.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decrypt = exports.encrypt = exports.createModelSnapshot = exports.getModelEvents = exports.restoreVersion = exports.getModelAtVersion = exports.getModel = exports.getModels = exports.patchModel = exports.updateModel = exports.createModel = exports.stream = exports.buildAdditionalPaths = exports.defaultSchema = exports.getEntityName = exports.buildFromModel = exports.eventNametoCamelCase = exports.findLinks = exports.build = void 0;
const filter_1 = __importDefault(require("lodash/filter"));
const get_1 = __importDefault(require("lodash/get"));
const has_1 = __importDefault(require("lodash/has"));
const isObject_1 = __importDefault(require("lodash/isObject"));
const merge_1 = __importDefault(require("lodash/merge"));
const omit_1 = __importDefault(require("lodash/omit"));
const c = __importStar(require("../../constants"));
const components_1 = __importDefault(require("./components"));
const MIME_APPLICATION_JSON = 'application/json';
function build(origin, models) {
let spec = (0, merge_1.default)({
paths: {
[`/stream/{model}/{source}`]: {
post: stream(models),
},
[`/stream/{model}/{source}/sse`]: {
get: stream(models, true),
},
},
}, origin);
for (const [modelName, model] of new Map([...models.MODELS.entries()].sort())) {
if (models.isInternalModel(modelName) === false) {
const links = findLinks(model, models);
spec = buildFromModel(spec, model, links);
}
}
return spec;
}
exports.build = build;
function walkKeysDeep(obj, cb, p = []) {
for (const k in obj) {
cb(k, p);
if ((0, isObject_1.default)(obj[k])) {
walkKeysDeep(obj[k], cb, [...p, k]);
}
}
}
function showHandler(event) {
if (event['x-show-handler'] !== true) {
return '';
}
let description = '';
if ((0, has_1.default)(event, 'handler')) {
description += `
### Handler
\`\`\`
function handler(state, event) {
${(0, get_1.default)(event, 'handler')}
}
\`\`\``;
}
return description;
}
function findLinks(model, models) {
const links = {};
walkKeysDeep(model.getSchema().model.properties, (k, p) => {
if (k.endsWith('_id') && k !== model.getCorrelationField()) {
const linkedModel = models.getModelByCorrelationField(k);
/* istanbul ignore next */
if (linkedModel !== null) {
links[k] = {
path: [
...p.filter((fragment) => !['items', 'properties'].includes(fragment)),
k,
].join('/'),
model: linkedModel,
};
}
}
});
return links;
}
exports.findLinks = findLinks;
function eventNametoCamelCase(str) {
return str
.toLowerCase()
.replace(/([-_][a-zA-Z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''));
}
exports.eventNametoCamelCase = eventNametoCamelCase;
function buildFromModel(spec, model, links) {
const collection = model.getCollectionName();
const schema = model.getSchema();
const modelConfig = model.getModelConfig();
const tagName = getEntityName(model);
const entityName = getEntityName(model, true);
const events = Object.keys(schema.events);
const additionnalPaths = buildAdditionalPaths(model);
const component = schema.model;
const processings = modelConfig.processings ?? [];
const processingsStr = processings
.map((processing, i) => {
const title = 'Activity ' +
tagName[0] +
'.' +
(i + 1) +
' - `' +
processing.field +
'` - ' +
processing.name;
let str = '### ' + title + '\n\n';
str += processing.purpose + '\n';
if (Array.isArray(processing.tokens)) {
str += '#### tokens\n';
str += processing.tokens.map((p) => `- \`${p}\``).join('\n');
str += '\n\n';
}
str += '#### Persons\n';
str += processing.persons.map((p) => `- ${p}`).join('\n');
str += '\n\n';
str += '#### Recipients\n';
str += processing.recipients.map((p) => `- ${p}`).join('\n');
str += '\n\n';
return str;
})
.join('\n');
return {
...spec,
components: {
...spec.components,
schemas: {
...spec.components.schemas,
[entityName]: {
...component,
required: component.required ?? undefined,
},
},
},
tags: [
...(spec.tags || []),
{
name: tagName,
description: `${modelConfig.description ??
'Routes available for the model <code>' + entityName + '</code>'}
Events available to this model: ${events.length > 0
? events.map((event) => '<code>' + event + '</code>').join(', ')
: 'none'}.
<details>
<summary>JSON Schema</summary>
\`\`\`json
${JSON.stringify(schema.model, null, 2)}
\`\`\`
</summary>
</details>
<details>
<summary>Data Processings (${processings.length})</summary>
${processingsStr}
</summary>
</details>
`,
},
],
paths: (0, merge_1.default)({}, spec.paths, {
[`/${collection}`]: {
get: getModels(model, links),
},
[`/${collection}/encrypt`]: {
post: encrypt(model, links),
},
[`/${collection}/decrypt`]: {
post: decrypt(model, links),
},
[`/${collection}/events`]: {
post: getModelEvents(model, links, { skipCorrelationField: true }),
},
[`/${collection}/{${model.getCorrelationField()}}`]: {
get: getModel(model, links),
},
[`/${collection}/{${model.getCorrelationField()}}/events`]: {
get: getModelEvents(model, links),
},
[`/${collection}/{${model.getCorrelationField()}}/snapshot`]: {
post: createModelSnapshot(model, links),
},
[`/${collection}/{${model.getCorrelationField()}}/{version}`]: {
get: getModelAtVersion(model, links),
},
}, events.includes(c.EVENT_TYPE_CREATED) && {
[`/${collection}`]: {
post: createModel(model, links),
},
}, events.includes(c.EVENT_TYPE_UPDATED) && {
[`/${collection}/{${model.getCorrelationField()}}`]: {
post: updateModel(model, links),
},
}, events.includes(c.EVENT_TYPE_PATCHED) && {
[`/${collection}/{${model.getCorrelationField()}}`]: {
patch: patchModel(model, links),
},
}, events.includes(c.EVENT_TYPE_RESTORED) && {
[`/${collection}/{${model.getCorrelationField()}}/{version}/restore`]: {
post: restoreVersion(model, links),
},
}, additionnalPaths),
};
}
exports.buildFromModel = buildFromModel;
function getEntityName(model, singular = false) {
let collection = typeof model === 'string' ? model : model.getCollectionName();
if (singular === true) {
collection = collection.replace(/s$/, '');
}
return collection.charAt(0).toUpperCase() + collection.slice(1);
}
exports.getEntityName = getEntityName;
function buildLinks(links) {
const _links = {};
for (const key in links) {
_links[getEntityName(links[key].model, true)] = {
operationId: eventNametoCamelCase(`GET_${getEntityName(links[key].model, true)}`),
parameters: {
/**
* @fixme Apply the camelCase only for GraphQL because in the current
* implementation, the link on REST is invalid
*/
[key]: `$response.body#/${eventNametoCamelCase(links[key].path)}`, // For GraphQL
},
};
}
return _links;
}
function defaultSchema(model) {
return {
tags: [getEntityName(model)],
security: [
{
apiKey: [],
},
],
responses: {
default: {
$ref: '#/components/responses/default',
},
},
};
}
exports.defaultSchema = defaultSchema;
function buildAdditionalPaths(model) {
const additionnalPaths = {};
const collection = model.getCollectionName();
const schema = model.getSchema();
const originalSchema = model.getOriginalSchema();
const events = schema.events;
const entityName = getEntityName(model, true);
for (const eventType in events) {
if (![
c.EVENT_TYPE_CREATED,
c.EVENT_TYPE_UPDATED,
c.EVENT_TYPE_RESTORED,
c.EVENT_TYPE_ROLLBACKED,
c.EVENT_TYPE_PATCHED,
c.EVENT_TYPE_ARCHIVED,
c.EVENT_TYPE_DELETED,
].includes(eventType)) {
const eventTypeLowered = eventType.toLocaleLowerCase();
for (const eventVersion in events[eventType]) {
const event = events[eventType][eventVersion];
const required = (0, filter_1.default)(event.required, (r) => !['v', 'type'].includes(r));
const routeSpec = (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`${entityName}_${eventType}`),
summary: event.title || `${eventTypeLowered}:v${eventVersion}`,
description: event.description ||
`Business event <code>${eventType}</code> at version <code>${eventVersion}</code>`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
],
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
...(0, omit_1.default)(event, 'handler', 'handlers', 'x-responses', 'x-show-handler', 'is_created', 'is_fhe', 'upsert'),
properties: {
...(0, omit_1.default)(event.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at'),
},
required: required.length ? required : undefined,
},
},
},
},
responses: {
200: {
description: 'Entity successfully updated',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
},
400: {
...components_1.default.responses[400],
description: 'Invalid Model / Event schema validation error',
},
409: components_1.default.responses[409],
422: components_1.default.responses[422],
},
});
routeSpec.description += showHandler(event);
const additionalResponses = {};
(event['x-responses'] || []).forEach((response) => {
additionalResponses[response.status] = {
description: response.description,
content: {
'application/json': {
example: {
status: response.status,
message: response.description,
details: response.details,
},
schema: {
$ref: '#/components/schemas/error',
},
},
},
};
});
routeSpec.responses = {
...routeSpec.responses,
...additionalResponses,
};
const originalEvent = (0, get_1.default)(originalSchema, `events.${eventType}.${eventVersion}`, {});
routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema.properties =
{
...routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema
.properties,
...(0, omit_1.default)(originalEvent.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at'),
};
additionnalPaths[`/${collection}/{${model.getCorrelationField()}}/${eventTypeLowered}/${eventVersion}`] = {
post: routeSpec,
};
}
}
}
return additionnalPaths;
}
exports.buildAdditionalPaths = buildAdditionalPaths;
function stream(models, isSSE = false) {
const entityName = 'all';
return (0, merge_1.default)({}, {
operationId: eventNametoCamelCase(`stream_${entityName}${isSSE === true ? '_sse' : ''}`),
summary: `Stream ${entityName} changes${isSSE === true ? ' SSE' : ''}`,
description: `Stream <code>${entityName}</code> changes in the database such as
creation or update${isSSE === true ? ' with Server Sent Events' : ''}.`,
parameters: [
{
in: 'path',
name: 'model',
description: 'Model name',
schema: {
type: 'string',
example: 'users',
enum: [
'all',
...Array.from(models.MODELS.keys()).filter((m) => models.isInternalModel(m) === false),
],
},
required: true,
},
{
in: 'path',
name: 'source',
description: 'Data source to stream',
schema: {
type: 'string',
enum: ['entities', 'events'],
example: 'entities',
},
required: true,
},
{
in: 'header',
name: 'output',
description: 'Expected output',
schema: {
type: 'string',
enum: ['entity', 'raw'],
example: 'entity',
},
required: false,
},
],
...(isSSE === false && {
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
type: 'object',
},
default: [],
example: [
{
$match: {
'fullDocument.firstname': 'John',
},
},
],
},
},
},
},
}),
responses: {
200: {
description: 'Stream of JSON objects',
content: {
[isSSE === true ? 'text/event-stream' : MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
type: 'object',
},
example: [
{
firstname: 'John',
},
],
},
},
},
},
400: components_1.default.responses[400],
404: components_1.default.responses[404],
},
});
}
exports.stream = stream;
function createModel(model, links) {
const schema = model.getSchema();
const event = schema.events[c.EVENT_TYPE_CREATED]['0_0_0'];
const required = (0, filter_1.default)(event.required, (r) => !['v', 'type'].includes(r));
const entityName = getEntityName(model, true);
const routeSpec = (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`create_${entityName}`),
summary: event.title || `Create a new ${entityName}`,
description: event.description ||
`Create a new <code>${entityName}</code> in the database.`,
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
...(0, omit_1.default)(event, 'handler'),
properties: {
...(0, omit_1.default)(event.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at', model.getIsReadonlyProperty(model.getModelConfig()), model.getIsArchivedProperty(model.getModelConfig()), model.getIsDeletedProperty(model.getModelConfig())),
},
required: required.length ? required : undefined,
},
},
},
},
responses: {
200: {
description: 'Entity successfully created',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: {
...components_1.default.responses[400],
description: 'Invalid Model / Event schema validation error',
},
409: components_1.default.responses[409],
},
});
routeSpec.description += showHandler(event);
const originalSchema = model.getOriginalSchema();
if (originalSchema) {
const originalEvent = (0, get_1.default)(originalSchema, `events.${c.EVENT_TYPE_CREATED}.0_0_0`, {});
routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema.properties = {
...routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema.properties,
...(0, omit_1.default)(originalEvent.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at'),
};
}
return routeSpec;
}
exports.createModel = createModel;
function updateModel(model, links) {
const schema = model.getSchema();
const event = schema.events[c.EVENT_TYPE_UPDATED]['0_0_0'];
const required = (0, filter_1.default)(event.required, (r) => !['v', 'type'].includes(r));
const entityName = getEntityName(model, true);
const routeSpec = (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`update_${entityName}`),
summary: event.title || `Update a ${entityName}`,
description: event.description ||
`Update an existing <code>${entityName}</code> already present in the database.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
],
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
...(0, omit_1.default)(event, 'handler'),
properties: {
...(0, omit_1.default)(event.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at', model.getIsReadonlyProperty(model.getModelConfig()), model.getIsArchivedProperty(model.getModelConfig()), model.getIsDeletedProperty(model.getModelConfig())),
},
required: required.length ? required : undefined,
},
},
},
},
responses: {
200: {
description: 'Entity successfully updated',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: {
...components_1.default.responses[400],
description: 'Invalid Model / Event schema validation error',
},
409: components_1.default.responses[409],
422: components_1.default.responses[422],
},
});
routeSpec.description += showHandler(event);
const originalSchema = model.getOriginalSchema();
if (originalSchema) {
const originalEvent = (0, get_1.default)(originalSchema, `events.${c.EVENT_TYPE_UPDATED}.0_0_0`, {});
routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema.properties = {
...routeSpec.requestBody.content[MIME_APPLICATION_JSON].schema.properties,
...(0, omit_1.default)(originalEvent.properties, 'v', 'type', 'version', 'json_patch', 'created_at', 'updated_at'),
};
}
return routeSpec;
}
exports.updateModel = updateModel;
function patchModel(model, links) {
const schema = model.getSchema();
const event = schema.events[c.EVENT_TYPE_PATCHED]['0_0_0'];
const entityName = getEntityName(model, true);
const routeSpec = (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`patch_${entityName}`),
summary: event.title || `Patch a ${entityName}`,
description: event.description ||
`Patch an existing <code>${entityName}</code> already present in the database.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
],
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'object',
required: ['json_patch'],
additionalProperties: false,
properties: {
json_patch: event.properties.json_patch,
},
},
},
},
},
responses: {
200: {
description: 'Entity successfully patched',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: {
...components_1.default.responses[400],
description: 'Invalid Model / Event schema validation error',
},
409: components_1.default.responses[409],
422: components_1.default.responses[422],
},
});
routeSpec.description += showHandler(event);
return routeSpec;
}
exports.patchModel = patchModel;
function getModels(model, links) {
const modelConfig = model.getModelConfig();
const originalSchema = model.getOriginalSchema();
const schema = model.getSchema();
const entityName = getEntityName(model);
const indexedFields = modelConfig.indexes?.reduce((current, index) => [...current, ...Object.keys(index.fields)], [modelConfig.correlation_field]);
const parameters = Object.keys(schema.model.properties)
.filter((key) => {
return ![
model.getIsArchivedProperty(modelConfig),
model.getIsDeletedProperty(modelConfig),
].includes(key);
})
.map((key) => {
const isIndexed = indexedFields?.includes(key);
const paramSchema = {
// Remove default value in the GET find query parameters:
...(0, omit_1.default)(schema.model.properties[key], 'default'),
description: `${isIndexed ? '<code>index</code> ' : ''}${schema.model.properties[key].description ?? ''}`,
};
const _schema = {
anyOf: [
paramSchema,
{
type: 'array',
items: paramSchema,
},
{
type: 'object',
},
],
};
return {
in: 'query',
name: key,
description: `${isIndexed ? '<code>index</code> ' : ''}${schema.model.properties[key].description ?? ''}`,
schema: _schema,
required: false,
};
});
// Raw queries support:
parameters.push({
in: 'query',
name: '_q',
description: 'MongoDb query in JSON stringified',
schema: {
type: 'string',
},
}, {
in: 'query',
name: '_must_hash',
description: 'Do we need to hash query values before find results if needed?',
schema: {
type: 'boolean',
},
}, {
in: 'header',
name: 'page',
...components_1.default.headers['pagination-page'],
}, {
in: 'header',
name: 'page-size',
...components_1.default.headers['pagination-size'],
}, {
in: 'header',
name: 'decrypt',
description: 'should we decipher the value',
schema: {
type: 'string',
enum: ['true', 'false'],
},
});
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`GET_${entityName}`),
summary: `Find ${entityName}`,
description: `Find <code>${entityName}</code> present in the database.`,
parameters,
responses: {
200: {
description: 'List of entities',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
$ref: `#/components/schemas/${getEntityName(model, true)}`,
},
},
},
},
headers: {
'correlation-field': {
description: 'Correlation field of the model',
schema: {
type: 'string',
},
},
page: {
...components_1.default.headers['pagination-page'],
},
'page-size': {
...components_1.default.headers['pagination-size'],
},
'page-count': {
...components_1.default.headers['pagination-count'],
},
count: {
description: 'Total number of items',
schema: {
type: 'integer',
minimum: 0,
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
},
});
}
exports.getModels = getModels;
function getModel(model, links) {
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`GET_${entityName}`),
summary: `Get a ${entityName}`,
description: `Get a specific <code>${entityName}</code> uniquely identified
by its <code>${model.getCorrelationField()}</code>.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
{
in: 'header',
name: 'decrypt',
description: 'should we decipher the value',
schema: {
type: 'string',
enum: ['true', 'false'],
},
},
],
responses: {
200: {
description: 'Entity successfully fetched',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
404: components_1.default.responses[404],
},
});
}
exports.getModel = getModel;
function getModelAtVersion(model, links) {
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`${entityName}_AT_VERSION`),
summary: `Get ${entityName} at version`,
description: `Get a specific version for a <code>${entityName}</code>.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
{
in: 'path',
name: 'version',
schema: {
type: 'string',
default: '0',
},
required: true,
description: 'Version',
},
],
responses: {
200: {
description: 'The model at the given version',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
404: components_1.default.responses[404],
},
});
}
exports.getModelAtVersion = getModelAtVersion;
function restoreVersion(model, links) {
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`RESTORE_${entityName}_AT_VERSION`),
summary: `Restore ${entityName} at version`,
description: `Restore a specific version of the <code>${entityName}</code>.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
{
in: 'path',
name: 'version',
schema: {
type: 'integer',
default: 0,
},
required: true,
description: 'Version',
},
],
responses: {
200: {
description: 'The model at the given version',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
404: components_1.default.responses[404],
409: components_1.default.responses[409],
},
});
}
exports.restoreVersion = restoreVersion;
function getModelEvents(model, links, options = { skipCorrelationField: false }) {
const entityName = getEntityName(model, true);
let operationId = eventNametoCamelCase(`${entityName}_EVENTS`);
let summary = `Get a ${entityName} events`;
let description = `Get all events attached to a <code>${entityName}</code>.`;
let parameters = [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
];
if (options.skipCorrelationField === true) {
operationId = eventNametoCamelCase(`${entityName}_ALL_EVENTS`);
summary = 'Get all events';
description = `Get all events created for this kind of entities whatever
the value of the correlation ID`;
parameters = [];
}
return (0, merge_1.default)({}, defaultSchema(model), {
operationId,
summary,
description,
parameters: [
...parameters,
{
in: 'header',
name: 'page',
...components_1.default.headers['pagination-page'],
},
{
in: 'header',
name: 'page-size',
...components_1.default.headers['pagination-size'],
},
],
responses: {
200: {
description: 'Entity events successfully fetched',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
type: 'object',
required: ['v', 'type', 'version'],
properties: {
type: c.COMPONENT_EVENT_TYPE,
v: c.COMPONENT_EVENT_TYPE_VERSION,
version: c.COMPONENT_EVENT_VERSION,
},
},
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
404: components_1.default.responses[404],
},
});
}
exports.getModelEvents = getModelEvents;
function createModelSnapshot(model, links) {
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`CREATE_${entityName}_SNAPSHOT`),
summary: `Create snapshot`,
description: `Create a snapshot of a <code>${entityName}</code> to keep
a frozen version in database.`,
parameters: [
{
in: 'path',
name: model.getCorrelationField(),
description: 'Correlation id',
schema: c.COMPONENT_CORRELATION_ID,
required: true,
},
],
responses: {
200: {
description: 'Snapshot successfully created',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
$ref: `#/components/schemas/${entityName}`,
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
422: components_1.default.responses[422],
},
});
}
exports.createModelSnapshot = createModelSnapshot;
function encrypt(model, links) {
const originalSchema = model.getOriginalSchema();
const schema = model.getSchema();
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`ENCRYPT_${entityName}`),
summary: `Encrypt fields`,
description: `Encrypt fields in <code>${entityName}</code>.`,
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
type: 'object',
properties: schema.model.properties,
},
},
},
},
},
responses: {
200: {
description: 'Encrypted fields',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
...(0, omit_1.default)((0, get_1.default)(originalSchema, 'model'), 'handler'),
required: undefined,
},
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
422: components_1.default.responses[422],
},
});
}
exports.encrypt = encrypt;
function decrypt(model, links) {
const originalSchema = model.getOriginalSchema();
const schema = model.getSchema();
const entityName = getEntityName(model, true);
return (0, merge_1.default)({}, defaultSchema(model), {
operationId: eventNametoCamelCase(`DECRYPT_${entityName}`),
summary: `Decrypt encrypted fields`,
description: `Decrypt fields in <code>${entityName}</code>.`,
requestBody: {
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
type: 'object',
properties: schema.model.properties,
},
},
},
},
},
responses: {
200: {
description: 'Decrypted fields',
content: {
[MIME_APPLICATION_JSON]: {
schema: {
type: 'array',
items: {
...(0, omit_1.default)((0, get_1.default)(originalSchema, 'model'), 'handler'),
additionalProperties: true,
required: undefined,
},
},
},
},
links: buildLinks(links),
},
400: components_1.default.responses[400],
422: components_1.default.responses[422],
},
});
}
exports.decrypt = decrypt;
//# sourceMappingURL=builder.js.map