@eang/core
Version:
eang - model driven enterprise event processing
263 lines • 8.8 kB
JavaScript
import { v7 as uuidv7 } from 'uuid';
class Entity {
id;
key;
typeOf;
instanceOf;
name;
childOf;
description;
data;
tags;
attributes;
createdBy;
createdAt;
updatedBy;
updatedAt;
get displayName() {
return this.name || this.key;
}
get instanceOfId() {
if (this.instanceOf) {
return `${this.typeOf}/${this.instanceOf}`;
}
return undefined;
}
constructor(entityOpts) {
this.id = entityOpts.id;
this.key = entityOpts.key;
this.typeOf = entityOpts.typeOf;
this.createdAt = Date.now(); // Default to now if not provided
if (entityOpts.instanceOf !== undefined)
this.instanceOf = entityOpts.instanceOf;
if (entityOpts.name !== undefined)
this.name = entityOpts.name;
if (entityOpts.childOf !== undefined)
this.childOf = entityOpts.childOf;
if (entityOpts.description !== undefined)
this.description = entityOpts.description;
if (entityOpts.data !== undefined)
this.data = entityOpts.data;
this.tags = entityOpts.tags || {};
this.attributes = entityOpts.attributes || {};
if (entityOpts.createdBy !== undefined)
this.createdBy = entityOpts.createdBy;
if (entityOpts.createdAt !== undefined)
this.createdAt = entityOpts.createdAt;
if (entityOpts.updatedBy !== undefined)
this.updatedBy = entityOpts.updatedBy;
if (entityOpts.updatedAt !== undefined)
this.updatedAt = entityOpts.updatedAt;
}
}
export class Obj extends Entity {
entityType = 'obj';
constructor(objOpts) {
let id;
let key;
let typeOf;
if (objOpts.id) {
id = objOpts.id;
const { key: parsedKey, typeOf: parsedTypeOf } = parseCombinedId(objOpts.id);
key = parsedKey;
typeOf = parsedTypeOf;
}
else {
typeOf = objOpts.typeOf;
key = objOpts.key || uuidv7();
id = typeOf + '/' + key;
}
objOpts.id = id;
objOpts.key = key;
objOpts.typeOf = typeOf;
super(objOpts);
}
}
export class Cnx extends Entity {
entityType = 'cnx';
typeOf;
fromObjId;
fromObjKey;
fromObjTypeOf;
fromObjInstanceOf;
toObjId;
toObjKey;
toObjTypeOf;
toObjInstanceOf;
constructor(cnxOpts) {
let id;
let key;
let typeOf;
if (cnxOpts.id) {
id = cnxOpts.id;
const { key: parsedKey, typeOf: parsedTypeOf } = parseCombinedId(cnxOpts.id);
key = parsedKey;
typeOf = parsedTypeOf ?? cnxOpts.typeOf;
if (cnxOpts.typeOf && parsedTypeOf !== cnxOpts.typeOf) {
throw new Error(`Type mismatch: expected ${cnxOpts.typeOf}, got ${parsedTypeOf}`);
}
}
else if (cnxOpts.typeOf) {
typeOf = cnxOpts.typeOf;
key = cnxOpts.key || uuidv7();
id = typeOf + '/' + key;
}
else {
throw new Error('Either id or typeOf must be provided for Cnx');
}
cnxOpts.id = id;
cnxOpts.key = key;
cnxOpts.typeOf = typeOf;
super(cnxOpts);
this.typeOf = typeOf;
if (cnxOpts.fromObjId) {
this.fromObjId = cnxOpts.fromObjId;
const { key: fromKey, typeOf: fromTypeOf } = parseCombinedId(cnxOpts.fromObjId);
this.fromObjKey = fromKey;
this.fromObjTypeOf = fromTypeOf;
}
else if (cnxOpts.fromObjKey && cnxOpts.fromObjTypeOf) {
this.fromObjKey = cnxOpts.fromObjKey;
this.fromObjTypeOf = cnxOpts.fromObjTypeOf;
this.fromObjId = cnxOpts.fromObjTypeOf + '/' + cnxOpts.fromObjKey;
}
else {
throw new Error('fromObjId or fromObjKey and fromObjTypeOf are required for Cnx');
}
if (cnxOpts.fromObjInstanceOf !== undefined) {
this.fromObjInstanceOf = cnxOpts.fromObjInstanceOf;
}
if (cnxOpts.toObjId) {
this.toObjId = cnxOpts.toObjId;
const { key: toKey, typeOf: toTypeOf } = parseCombinedId(cnxOpts.toObjId);
this.toObjKey = toKey;
this.toObjTypeOf = toTypeOf;
}
else if (cnxOpts.toObjKey && cnxOpts.toObjTypeOf) {
this.toObjKey = cnxOpts.toObjKey;
this.toObjTypeOf = cnxOpts.toObjTypeOf;
this.toObjId = cnxOpts.toObjTypeOf + '/' + cnxOpts.toObjKey;
}
else {
throw new Error('toObjId or toObjKey and toObjTypeOf are required for Cnx');
}
if (cnxOpts.toObjInstanceOf !== undefined) {
this.toObjInstanceOf = cnxOpts.toObjInstanceOf;
}
}
}
export const EangEventType = {
create: 'create',
update: 'update',
delete: 'delete',
start: 'start',
stop: 'stop'
};
export class EntityEvent {
entity;
eventType;
context;
tenant;
organizationalUnit;
user;
constructor(entity, eventType, context, options) {
this.entity = entity;
this.eventType = eventType;
this.context = context;
if (options?.tenant !== undefined) {
this.tenant = options.tenant;
}
if (options?.organizationalUnit !== undefined) {
this.organizationalUnit = options.organizationalUnit;
}
if (options?.user !== undefined) {
this.user = options.user;
}
}
static parse(entity, eventType, context, options) {
return new EntityEvent(entity, eventType, context, options);
}
static create(entity, options) {
if (!entity.createdAt) {
entity.createdAt = Date.now();
}
return new EntityEvent(entity, EangEventType.create, undefined, options);
}
static update(entity, context, options) {
entity.updatedAt = Date.now();
// Apply new values to the entity before creating the event
return new EntityEvent(entity, EangEventType.update, context, options);
}
static delete(entity, options) {
return new EntityEvent(entity, EangEventType.delete, undefined, options);
}
static start(entity, context, options) {
if ('createdAt' in entity && !entity.createdAt) {
entity.createdAt = Date.now();
}
if (!('startedAt' in entity) || !entity.startedAt) {
entity.startedAt = Date.now();
}
return new EntityEvent(entity, EangEventType.start, context, options);
}
static stop(entity, context, options) {
if (!('stoppedAt' in entity) || !entity.stoppedAt) {
entity.stoppedAt = Date.now();
}
return new EntityEvent(entity, EangEventType.stop, context, options);
}
}
export function parseCombinedId(id) {
const sepIdx = id.indexOf('/');
if (sepIdx === -1 || sepIdx === 0 || sepIdx === id.length - 1) {
throw new Error(`Invalid ID format: ${id}`);
}
return { typeOf: id.slice(0, sepIdx), key: id.slice(sepIdx + 1) };
}
/**
* Converts an entity (Obj or Cnx) to ArangoDB document format
* @param entity The entity to convert
* @returns ArangoDB document with proper _key, _from, _to fields
*/
export function toArangoDoc(entity) {
// Prepare the document for ArangoDB
const arangoDoc = {
_key: entity.key,
...entity
};
// Remove fields that shouldn't be stored or are redundant
delete arangoDoc.key;
delete arangoDoc.entityType;
delete arangoDoc.id;
// Remove empty tags and attributes objects
if (arangoDoc.tags &&
typeof arangoDoc.tags === 'object' &&
Object.keys(arangoDoc.tags).length === 0) {
delete arangoDoc.tags;
}
if (arangoDoc.attributes &&
typeof arangoDoc.attributes === 'object' &&
Object.keys(arangoDoc.attributes).length === 0) {
delete arangoDoc.attributes;
}
// Handle connection entities differently
if (entity.entityType === 'cnx') {
// For connections, we need to set _from and _to for ArangoDB edge collection
arangoDoc._from = entity.fromObjId;
arangoDoc._to = entity.toObjId;
// Remove the redundant connection fields that ArangoDB doesn't need
delete arangoDoc.fromObjId;
delete arangoDoc.fromObjKey;
delete arangoDoc.fromObjTypeOf;
delete arangoDoc.fromObjInstanceOf;
delete arangoDoc.toObjId;
delete arangoDoc.toObjKey;
delete arangoDoc.toObjTypeOf;
delete arangoDoc.toObjInstanceOf;
}
else {
delete arangoDoc.typeOf;
}
return arangoDoc;
}
//# sourceMappingURL=entity.js.map