UNPKG

@noggin/elastic-noggin-sdk

Version:
441 lines (334 loc) 9.96 kB
import { IEno, IField, II18n, Tip } from "./models/types"; import { Eno } from "./models/Eno"; import { hashSidStringify } from "./sidStringify"; import { cloneDeep, isArray, isEqual, pullAllBy } from "lodash"; import dataConstants from "./constants"; import shajs from "sha.js"; const EMPTY_NONCE_TYPES = [ "var", "query/dimension", "op/query", "op/query/dimension", "op/pull", "op/merge", "op/formula", "op/session", "op/watch/register", "op/watch/unregister", "op/auth/gen-token", "op/auth/reset-token", "op/auth/register", ]; export class EnoFactory { private _proto: IEno = null; private _patchTargetTip: Tip = null; private _useEmptyNonce = false; private _useTipNonce = false; private _useKnownNonce: string|null = null; constructor(typeOrEnoProto: Tip | IEno = null, security: Tip = null) { if (typeOrEnoProto && typeof typeOrEnoProto !== "string") { this.setProto(typeOrEnoProto); return; } this.reset(typeOrEnoProto as Tip, security); } public makeEno(): Eno { const isValid = this._isSettingValid(); if (!isValid) { throw new Error( "Eno Factory setting has not enough information to make an eno." ); } this._cleanField(); if (this._useEmptyNonce || this._emptyNonceRequired()) { this._proto.source.nonce = ''; } else if (this._useTipNonce && this._patchTargetTip) { this._proto.source.nonce = this._patchTargetTip; } else if (this._useKnownNonce !== null) { this._proto.source.nonce = this._useKnownNonce; } else { this._proto.source.nonce = this._getRandomNonce(); } this._generateSid(); this._cleanTransaction(); this._proto.tip = this._patchTargetTip || this._proto.sid; return new Eno(this._proto); } private _getRandomNonce() { return Math.random() + ""; } private _cleanTransaction() { if (this._proto.clientT === null) { this._resetClientT(); } const clientT = this._proto.clientT; clientT.sequence = clientT.sequence || 1; clientT.createdDate = clientT.modifiedDate = new Date().valueOf(); this._proto.serverT = null; } private _emptyNonceRequired(): boolean { return EMPTY_NONCE_TYPES.indexOf(this._proto.source.type) > -1; } private _generateSid() { let sha256 = shajs("sha256"); hashSidStringify(sha256, this._proto.source); this._proto.sid = sha256.digest("hex"); sha256 = null; } private _cleanField() { this._proto.source.field = this._proto.source.field.filter((field) => { return ( (field.value && field.value.length !== 0) || (field.i18n && _containsNonEmptyValue()) || field.formula ); function _containsNonEmptyValue(): boolean { let nonEmptyValueFound = false; for (let i = 0; i < field.i18n.length; i++) { if (field.i18n[i].value && field.i18n[i].value.length > 0) { nonEmptyValueFound = true; break; } } return nonEmptyValueFound; } }); } private _isSettingValid(): boolean { return !( this._proto.source === null || this._proto.source.type === null || this._proto.source.security === null ); } public reset(type: Tip = null, security: Tip = null): EnoFactory { this._patchTargetTip = null; this._useEmptyNonce = false; this._useTipNonce = false; this._useKnownNonce = null; this._proto = { source: { deleted: false, type, security, parent: [], field: [], nonce: this._getRandomNonce(), }, tip: null, sid: null, serverT: null, clientT: null, }; return this; } public setProto(eno: IEno): EnoFactory { this.reset(); if (eno.clientT) { this._proto.clientT = cloneDeep(eno.clientT); } if (eno.source) { this._proto.source = cloneDeep(eno.source); } return this; } public setProtoToPatch(eno: IEno): EnoFactory { if (!eno.source) { throw new Error("You can't patch acknowledgement"); } this.reset(); this._patchTargetTip = eno.tip; if (eno.clientT) { this._proto.clientT = cloneDeep(eno.clientT); this._proto.clientT.sequence++; } else { this._resetClientT(); this._proto.clientT.sequence = 1; } this._proto.source = cloneDeep(eno.source); this._proto.source.parent = [eno.sid]; return this; } public resetFields(): EnoFactory { this._proto.source.field = []; return this; } public setWellKnownTip(tip: Tip): EnoFactory { this._patchTargetTip = tip; return this; } public setType(type: Tip): EnoFactory { this._proto.source.field = this._proto.source.type !== type ? [] : this._proto.source.field; this._proto.source.type = type; return this; } public useEmptyNonce(): EnoFactory { this._useEmptyNonce = true; return this; } public useTipNonce(): EnoFactory { this._useTipNonce = true; return this; } public useRandomNonce(): EnoFactory { this._useTipNonce = false; this._useEmptyNonce = false; this._useKnownNonce = null; return this; } public useKnownNonce(nonce: string|null): EnoFactory { this._useKnownNonce = nonce; return this; } public setI18nValue(fieldTip: Tip, lang: string, value: string[]) { value = value.filter(this._normalizeValuesFilter); let fieldFound = false; for (let i = 0; i < this._proto.source.field.length; i++) { const field = this._proto.source.field[i]; if (field.tip === fieldTip) { fieldFound = true; this._updateExistingI18n(this._proto.source.field[i], lang, value); break; } } if (fieldFound) { return this; } this._proto.source.field.push({ tip: fieldTip, i18n: [{ lang, value }] }); return this; } private _updateExistingI18n(field: IField, lang: string, value: string[]) { field.i18n = field.i18n || []; if (field.value) { field.i18n = [{ lang: dataConstants.LANG_DEFAULT, value: field.value }]; delete field.value; } if (!isArray(value) || value.length === 0) { pullAllBy(field.i18n, [{ lang }], "lang"); return; } const i18nValue: II18n = field.i18n.find( (i18n: II18n) => i18n.lang === lang ); if (i18nValue && isEqual(i18nValue.value, value)) { return; } // Invalidate the values for other locales when the default locale value is updated. if (lang === dataConstants.LANG_DEFAULT) { field.i18n = [{ lang, value }]; return; } let langFound = false; for (const i18n of field.i18n) { if (i18n.lang === lang) { langFound = true; i18n.value = value; break; } } if (!langFound) { field.i18n.push({ lang, value }); } } // Not recommended to use this method to set i18n field public setField( newFieldOrTip: string | IField, value?: string[] ): EnoFactory { let newField = typeof newFieldOrTip === "string" ? { tip: newFieldOrTip, value } : newFieldOrTip; newField = this._normalizeIField(newField); let fieldFound = false; for (let i = 0; i < this._proto.source.field.length; i++) { const field = this._proto.source.field[i]; if (field.tip === newField.tip) { fieldFound = true; this._proto.source.field[i] = newField; break; } } if (fieldFound) { return this; } this._proto.source.field.push(newField); return this; } public setFieldFormula(fieldTip: Tip, formula: string): EnoFactory { let newField: IField = { tip: fieldTip, formula: [formula] }; newField = this._normalizeIField(newField); let fieldFound = false; for (let i = 0; i < this._proto.source.field.length; i++) { const field = this._proto.source.field[i]; if (field.tip === newField.tip) { fieldFound = true; this._proto.source.field[i] = newField; break; } } if (fieldFound) { return this; } this._proto.source.field.push(newField); return this; } private _normalizeIField(field: IField): IField { if (!field.i18n && !field.formula) { field.value = field.value === null || field.value === undefined ? [] : field.value; field.value = field.value.filter(this._normalizeValuesFilter); return field; } if (field.i18n) { field.i18n.forEach((i18n) => { i18n.value = i18n.value === null || i18n.value === undefined ? [] : i18n.value; i18n.value = i18n.value.filter(this._normalizeValuesFilter); }); } return field; } private _normalizeValuesFilter(val: string): boolean { return val !== null && val !== undefined; } public setFields(newFields: IField[]): EnoFactory { newFields.forEach((newField) => { this.setField(newField); }); return this; } public setSecurity(security: Tip): EnoFactory { this._proto.source.security = security; return this; } public setDeleted(deleted: boolean): EnoFactory { this._proto.source.deleted = deleted; this.resetFields(); return this; } public setBranch(branch: Tip = dataConstants.BRANCH_MASTER): EnoFactory { if (this._proto.clientT === null) { this._resetClientT(branch); return this; } this._proto.clientT.branch = branch; return this; } private _resetClientT(branch: Tip = dataConstants.BRANCH_MASTER) { this._proto.clientT = { branch, sequence: null, createdDate: null, modifiedDate: null, }; } public setSequence(sequence: number): EnoFactory { if (this._proto.clientT === null) { this._resetClientT(); } this._proto.clientT.sequence = sequence; return this; } }