{"version":3,"sources":["../../../packages/core/base/versioned-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAc,MAAM,MAAM,CAAC;AAI9C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,UAAU;IAEpD;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IAEpC;;;OAGG;IACH,IAAI,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B,CAAC,CAAC,SAAS,eAAe;IACjE,KAAI,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,uBAAuB,GAAG,CAAC,CAAC;CAClF;AAED;;GAEG;AACH,8BAAsB,eAAe;IA+CgB,OAAO,CAAC,QAAQ;IAA2B,SAAS,CAAC,cAAc,CAAC;IA7CrH,SAAS,KAAK,aAAa,WAE1B;IAED;;OAEG;IACH,SAAS,CAAC,QAAQ,KAAK,aAAa,IAAI,MAAM,CAAC;IAE/C;;OAEG;IACI,cAAc,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,UAAU,CAAa;IAE/B;;OAEG;WACW,eAAe,IAAI,oBAAoB;IAIrD;;;OAGG;WACW,uBAAuB,CAAC,GAAG,EAAE,oBAAoB,GAAG,oBAAoB;IAOtF;;;;;;;OAOG;gBACS,aAAa,EAAE,oBAAoB,EAAU,QAAQ,EAAE,uBAAuB,EAAY,cAAc,CAAC,KAAA;IAIrH;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA+DlB;;OAEG;IACI,MAAM,IAAI,oBAAoB;IAIrC;;OAEG;IACI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC;IAI/B;;;OAGG;IACI,OAAO,CAAC,EAAE,EAAE,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;IAY9C;;OAEG;IACH,SAAS,CAAC,KAAK,IAAI,IAAI;IAIvB;;;OAGG;IACI,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAU1C;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI;IAElC;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAiB7C;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI;CAe7D","file":"versioned-object.d.ts","sourcesContent":["import { Observable, throwError } from 'rxjs';\r\nimport { catchError } from 'rxjs/operators';\r\nimport { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { JsonObject, JsonValue } from './json';\r\n\r\n/**\r\n * Defines a wrapper for an object and its current version\r\n * All versioned data should end up in this format\r\n */\r\nexport interface PlainVersionedObject extends JsonObject {\r\n\r\n    /**\r\n     * The properties of the object\r\n     */\r\n    properties: JsonObject;\r\n\r\n    /**\r\n     * The version of the object\r\n     */\r\n    version: number;\r\n}\r\n\r\n/**\r\n * Defines the handlers for a versioned object\r\n */\r\nexport interface VersionedObjectHandlers {\r\n\r\n    /**\r\n     * The Save handler. Called by the consumer when changes to a versioned object need to be saved.\r\n     * @param objectWrapper the wrapper object to save\r\n     */\r\n    save: (object: PlainVersionedObject) => Observable<void>;\r\n}\r\n\r\n/**\r\n * Defines the constructor of a versioned object. Used to dynamically construct classes that extend VersionedObject\r\n */\r\nexport interface VersionedObjectConstructor<T extends VersionedObject> {\r\n    new(objectWrapper: PlainVersionedObject, handlers: VersionedObjectHandlers): T;\r\n}\r\n\r\n/**\r\n * Defines an object that can be upgraded from older versions to the current version upon initialization.\r\n */\r\nexport abstract class VersionedObject {\r\n\r\n    protected get logSourceName() {\r\n        return 'VersionedObject';\r\n    }\r\n\r\n    /**\r\n     * Getter for the latest version of the object.\r\n     */\r\n    protected abstract get latestVersion(): number;\r\n\r\n    /**\r\n     * The current version of the object\r\n     */\r\n    public currentVersion: number;\r\n\r\n    /**\r\n     * The properties of the object\r\n     */\r\n    private properties: JsonObject;\r\n\r\n    /**\r\n     * Gets an initialized empty versioned object wrapper\r\n     */\r\n    public static getEmptyWrapper(): PlainVersionedObject {\r\n        return { version: null, properties: {} };\r\n    }\r\n\r\n    /**\r\n     * Returns an empty versioned object if the passed in object is not correctly versioned.\r\n     * Otherwise, returns the passed in object.\r\n     */\r\n    public static ensureIsVersionedObject(obj: PlainVersionedObject): PlainVersionedObject {\r\n        if (!obj || MsftSme.isNullOrUndefined(obj.version)) {\r\n            return VersionedObject.getEmptyWrapper();\r\n        }\r\n        return obj;\r\n    }\r\n\r\n    /**\r\n     * Initializes a new instance of a VersionedObject\r\n     * If the current version of the object is less than the latest version,\r\n     * then 'upgrade()' will be called until the version is updated to the latest version.\r\n     * An error will be thrown if upgrade is called and the version is not moved forward by at least 1.\r\n     * @param objectWrapper the simplified version of the plain object with versioning\r\n     * @param handlers The handlers to modify the plain Object\r\n     */\r\n    constructor(objectWrapper: PlainVersionedObject, private handlers: VersionedObjectHandlers, protected gatewayService?) {\r\n        this.initialize(objectWrapper, handlers);\r\n    }\r\n\r\n    /**\r\n     * Initializes this versioned object\r\n     * @param objectWrapper the simplified version of the plain object with versioning\r\n     * @param handlers The handlers to modify the plain Object\r\n     */\r\n    private initialize(objectWrapper: PlainVersionedObject, handlers: VersionedObjectHandlers) {\r\n        // verify arguments\r\n        if (MsftSme.isNullOrUndefined(objectWrapper)\r\n            || MsftSme.isNullOrUndefined(objectWrapper.properties)\r\n            || MsftSme.isNullOrUndefined(handlers)) {\r\n            Logging.log({\r\n                level: LogLevel.Error,\r\n                message: `${this.logSourceName} Invalid Arguments: objectWrapper: ${objectWrapper}, handlers: ${handlers}`,\r\n                params: {\r\n                    latestVersion: this.latestVersion,\r\n                    objectType: this.logSourceName\r\n                },\r\n                source: 'VersionedObject.ctor'\r\n            });\r\n            return;\r\n        }\r\n        // save version and properties\r\n        this.currentVersion = objectWrapper.version;\r\n        this.properties = MsftSme.deepCopy(objectWrapper.properties);\r\n\r\n        // ensure that the getter for latest version has been defined\r\n        if (MsftSme.isNullOrUndefined(this.latestVersion)) {\r\n            Logging.log({\r\n                level: LogLevel.Error,\r\n                message: `${this.logSourceName} does not properly extend VersionedObject. latestVersion getter is ${this.latestVersion}'`,\r\n                params: {\r\n                    latestVersion: this.latestVersion,\r\n                    objectType: this.logSourceName\r\n                },\r\n                source: 'VersionedObject.ctor'\r\n            });\r\n            return;\r\n        }\r\n\r\n        let prevVersion = MsftSme.isNullOrUndefined(this.currentVersion) ? -1 : this.currentVersion;\r\n        // upgrade the current version until it is the latest version\r\n        while (MsftSme.isNullOrUndefined(this.currentVersion) || this.currentVersion < this.latestVersion) {\r\n            // upgrade the object\r\n            this.upgrade();\r\n\r\n            // check if upgrade succeeded (did not downgrade and did not upgrade past latest version)\r\n            if (prevVersion >= this.currentVersion || this.currentVersion > this.latestVersion) {\r\n                // upgrade didnt work as intended as the version is now invalid.\r\n                Logging.log({\r\n                    level: LogLevel.Error,\r\n                    message: `Calling 'upgrade()' did not upgrade the object correctly'`,\r\n                    params: {\r\n                        previousVersion: prevVersion,\r\n                        currentVersion: this.currentVersion,\r\n                        upgradingToVersion: this.latestVersion,\r\n                        objectType: this.logSourceName\r\n                    },\r\n                    source: 'VersionedObject.ctor'\r\n                });\r\n                // stop trying to upgrade\r\n                break;\r\n            }\r\n\r\n            // remember the version before cycling again\r\n            prevVersion = this.currentVersion;\r\n        }\r\n    }\r\n\r\n    /**\r\n     *  Converts this versioned object to its pure json representation\r\n     */\r\n    public toJson(): PlainVersionedObject {\r\n        return { properties: this.properties, version: this.currentVersion };\r\n    }\r\n\r\n    /**\r\n     *  Saves the current properties\r\n     */\r\n    public save(): Observable<void> {\r\n        return this.handlers.save(this.toJson());\r\n    }\r\n\r\n    /**\r\n     * Attempts to save the properties after executing a function.\r\n     * If saving fails, the properties is reverted to its previous state before emitting the error\r\n     */\r\n    public trySave(fn: Function): Observable<void> {\r\n        const backup = Object.assign({}, this.properties);\r\n        fn();\r\n\r\n        return this.save()\r\n            .pipe(catchError((error) => {\r\n                Object.assign(this.properties, backup);\r\n                // rethrow the error\r\n                return throwError(() => error);\r\n            }));\r\n    }\r\n\r\n    /**\r\n     *  Clears the current properties\r\n     */\r\n    protected clear(): void {\r\n        this.properties = {};\r\n    }\r\n\r\n    /**\r\n     * Clears a specific value from the setting.\r\n     * @param key a property key\r\n     */\r\n    public clearProperty(key: string): boolean {\r\n        if (!MsftSme.isNullOrUndefined(key) && !MsftSme.isNullOrUndefined(this.properties)\r\n            && !MsftSme.isNullOrUndefined(!this.properties[key])) {\r\n            delete this.properties[key];\r\n            return true;\r\n        } else {\r\n            return false;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Attempts to upgrade the current version of the object at least one version toward the latest version.\r\n     * if this.currentVersion is null or undefined, then the upgrade should initialize to the latest version\r\n     * this is called itterativly untill the current version is equal to the latest version\r\n     */\r\n    protected abstract upgrade(): void;\r\n\r\n    /**\r\n     * Gets a property from 'properties'\r\n     * @param key the property key\r\n     * @returns the properties value\r\n     */\r\n    protected getProperty(key: string): JsonValue {\r\n        if (!MsftSme.isNullOrUndefined(this.properties)) {\r\n            return this.properties[key];\r\n        } else {\r\n            Logging.log({\r\n                level: LogLevel.Error,\r\n                message: `Could not find a property with the name: '${key}'. Please wait for object properties to be defined.`,\r\n                params: {\r\n                    key: key,\r\n                    objectType: this.logSourceName\r\n                },\r\n                source: 'VersionedObject.getProperty'\r\n            });\r\n        }\r\n        return null;\r\n    }\r\n\r\n    /**\r\n     * Sets a property in 'properties'\r\n     * @param key the property key\r\n     * @param value the new value\r\n     */\r\n    protected setProperty(key: string, value: JsonValue): void {\r\n        if (!MsftSme.isNullOrUndefined(this.properties)) {\r\n            this.properties[key] = value;\r\n        } else {\r\n            Logging.log({\r\n                level: LogLevel.Error,\r\n                message: `Could not find a property with the name: '${key}'. Please wait for object properties to be defined.`,\r\n                params: {\r\n                    key: key,\r\n                    objectType: this.logSourceName\r\n                },\r\n                source: 'VersionedObject.setProperty'\r\n            });\r\n        }\r\n    }\r\n}\r\n"]}