@twin.org/synchronised-storage-service 0.0.3-next.9 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/es/helpers/blobStorageHelper.js +1 -1
- package/dist/es/helpers/blobStorageHelper.js.map +1 -1
- package/dist/es/helpers/changeSetHelper.js +3 -3
- package/dist/es/helpers/changeSetHelper.js.map +1 -1
- package/dist/es/helpers/localSyncStateHelper.js +7 -7
- package/dist/es/helpers/localSyncStateHelper.js.map +1 -1
- package/dist/es/helpers/remoteSyncStateHelper.js +14 -9
- package/dist/es/helpers/remoteSyncStateHelper.js.map +1 -1
- package/dist/es/helpers/versions.js +9 -0
- package/dist/es/helpers/versions.js.map +1 -1
- package/dist/es/restEntryPoints.js +3 -0
- package/dist/es/restEntryPoints.js.map +1 -1
- package/dist/es/synchronisedStorageRoutes.js +4 -2
- package/dist/es/synchronisedStorageRoutes.js.map +1 -1
- package/dist/es/synchronisedStorageService.js +14 -11
- package/dist/es/synchronisedStorageService.js.map +1 -1
- package/dist/types/helpers/blobStorageHelper.d.ts +1 -1
- package/dist/types/helpers/changeSetHelper.d.ts +3 -3
- package/dist/types/helpers/localSyncStateHelper.d.ts +5 -5
- package/dist/types/helpers/remoteSyncStateHelper.d.ts +10 -19
- package/dist/types/helpers/versions.d.ts +9 -0
- package/dist/types/restEntryPoints.d.ts +3 -0
- package/dist/types/synchronisedStorageService.d.ts +3 -3
- package/docs/changelog.md +229 -76
- package/docs/examples.md +80 -1
- package/docs/open-api/spec.json +17 -21
- package/docs/reference/classes/SyncSnapshotEntry.md +12 -12
- package/docs/reference/classes/SynchronisedStorageService.md +9 -9
- package/docs/reference/interfaces/ISyncPointerStore.md +2 -2
- package/docs/reference/interfaces/ISyncSnapshot.md +7 -7
- package/docs/reference/interfaces/ISyncState.md +3 -3
- package/docs/reference/interfaces/ISynchronisedStorageServiceConfig.md +13 -13
- package/docs/reference/interfaces/ISynchronisedStorageServiceConstructorOptions.md +19 -19
- package/docs/reference/variables/restEntryPoints.md +2 -0
- package/package.json +20 -22
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Synchronised Storage Service
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This package provides the synchronisation service layer responsible for tracking local and remote change state, scheduling update cycles, and handling trusted interactions around key and change propagation.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -151,7 +151,7 @@ export class BlobStorageHelper {
|
|
|
151
151
|
/**
|
|
152
152
|
* Remove a blob from storage.
|
|
153
153
|
* @param blobId The id of the blob to remove.
|
|
154
|
-
* @returns
|
|
154
|
+
* @returns A promise that resolves when the blob is removed.
|
|
155
155
|
*/
|
|
156
156
|
async removeBlob(blobId) {
|
|
157
157
|
await this._logging?.log({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blobStorageHelper.js","sourceRoot":"","sources":["../../../src/helpers/blobStorageHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IBlobStorageConnector } from \"@twin.org/blob-storage-models\";\nimport {\n\tBaseError,\n\tCompression,\n\tCompressionType,\n\tGeneralError,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type IVaultConnector, VaultEncryptionType } from \"@twin.org/vault-models\";\n\n/**\n * Class for performing blob storage operations.\n */\nexport class BlobStorageHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<BlobStorageHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The vault connector.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The blob storage connector to use.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageConnector: IBlobStorageConnector;\n\n\t/**\n\t * The id of the vault key to use for encrypting/decrypting blobs.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageEncryptionKeyId: string;\n\n\t/**\n\t * Is this a trusted node.\n\t * @internal\n\t */\n\tprivate readonly _isTrustedNode: boolean;\n\n\t/**\n\t * The node id of this node.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of BlobStorageHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param vaultConnector The vault connector to use for for the encryption key.\n\t * @param blobStorageConnector The blob storage component to use.\n\t * @param blobStorageEncryptionKeyId The id of the vault key to use for encrypting/decrypting blobs.\n\t * @param isTrustedNode Is this a trusted node.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\tvaultConnector: IVaultConnector,\n\t\tblobStorageConnector: IBlobStorageConnector,\n\t\tblobStorageEncryptionKeyId: string,\n\t\tisTrustedNode: boolean\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._vaultConnector = vaultConnector;\n\t\tthis._blobStorageConnector = blobStorageConnector;\n\t\tthis._blobStorageEncryptionKeyId = blobStorageEncryptionKeyId;\n\t\tthis._isTrustedNode = isTrustedNode;\n\t}\n\n\t/**\n\t * Set the node id of this node.\n\t * @param nodeId The node id to set.\n\t */\n\tpublic setNodeId(nodeId: string): void {\n\t\tthis._nodeId = nodeId;\n\t}\n\n\t/**\n\t * Load a blob from storage.\n\t * @param blobId The id of the blob to apply.\n\t * @returns The blob.\n\t */\n\tpublic async loadBlob<T>(blobId: string): Promise<T | undefined> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"loadBlob\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tconst encryptedBlob = await this._blobStorageConnector.get(blobId);\n\n\t\t\tif (Is.uint8Array(encryptedBlob)) {\n\t\t\t\tconst compressedBlob = await this._vaultConnector.decrypt(\n\t\t\t\t\t`${this._nodeId}/${this._blobStorageEncryptionKeyId}`,\n\t\t\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\t\t\tencryptedBlob\n\t\t\t\t);\n\n\t\t\t\tconst decompressedBlob = await Compression.decompress(compressedBlob, CompressionType.Gzip);\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"loadedBlob\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tblobId\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn ObjectHelper.fromBytes<T>(decompressedBlob);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"loadBlobFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"loadBlobEmpty\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Save a blob.\n\t * @param blob The blob to save.\n\t * @returns The id of the blob.\n\t */\n\tpublic async saveBlob<T>(blob: T): Promise<string> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"saveBlob\"\n\t\t});\n\n\t\tif (!this._isTrustedNode) {\n\t\t\tthrow new GeneralError(BlobStorageHelper.CLASS_NAME, \"notTrustedNode\");\n\t\t}\n\n\t\tconst compressedBlob = await Compression.compress(\n\t\t\tObjectHelper.toBytes(blob),\n\t\t\tCompressionType.Gzip\n\t\t);\n\n\t\tconst encryptedBlob = await this._vaultConnector.encrypt(\n\t\t\t`${this._nodeId}/${this._blobStorageEncryptionKeyId}`,\n\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\tcompressedBlob\n\t\t);\n\n\t\ttry {\n\t\t\tconst blobId = await this._blobStorageConnector.set(encryptedBlob);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"savedBlob\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn blobId;\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"saveBlobFailed\",\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Remove a blob from storage.\n\t * @param blobId The id of the blob to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeBlob(blobId: string): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"removeBlob\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tawait this._blobStorageConnector.remove(blobId);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"removedBlob\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"removeBlobFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"removeBlobEmpty\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"blobStorageHelper.js","sourceRoot":"","sources":["../../../src/helpers/blobStorageHelper.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,SAAS,EACT,WAAW,EACX,eAAe,EACf,YAAY,EACZ,EAAE,EACF,YAAY,EACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAwB,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEnF;;GAEG;AACH,MAAM,OAAO,iBAAiB;IAC7B;;OAEG;IACI,MAAM,CAAU,UAAU,uBAAuC;IAExE;;;OAGG;IACc,QAAQ,CAAqB;IAE9C;;;OAGG;IACc,eAAe,CAAkB;IAElD;;;OAGG;IACc,qBAAqB,CAAwB;IAE9D;;;OAGG;IACc,2BAA2B,CAAS;IAErD;;;OAGG;IACc,cAAc,CAAU;IAEzC;;;OAGG;IACK,OAAO,CAAU;IAEzB;;;;;;;OAOG;IACH,YACC,OAAsC,EACtC,cAA+B,EAC/B,oBAA2C,EAC3C,0BAAkC,EAClC,aAAsB;QAEtB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;QACtC,IAAI,CAAC,qBAAqB,GAAG,oBAAoB,CAAC;QAClD,IAAI,CAAC,2BAA2B,GAAG,0BAA0B,CAAC;QAC9D,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACrC,CAAC;IAED;;;OAGG;IACI,SAAS,CAAC,MAAc;QAC9B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,QAAQ,CAAI,MAAc;QACtC,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;YACpC,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE;gBACL,MAAM;aACN;SACD,CAAC,CAAC;QAEH,IAAI,CAAC;YACJ,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAEnE,IAAI,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClC,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CACxD,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,2BAA2B,EAAE,EACrD,mBAAmB,CAAC,gBAAgB,EACpC,aAAa,CACb,CAAC;gBAEF,MAAM,gBAAgB,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,cAAc,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;gBAC5F,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;oBACxB,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;oBACpC,OAAO,EAAE,YAAY;oBACrB,IAAI,EAAE;wBACL,MAAM;qBACN;iBACD,CAAC,CAAC;gBAEH,OAAO,YAAY,CAAC,SAAS,CAAI,gBAAgB,CAAC,CAAC;YACpD,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,iBAAiB,CAAC,UAAU;gBACpC,OAAO,EAAE,gBAAgB;gBACzB,IAAI,EAAE;oBACL,MAAM;iBACN;gBACD,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;aACjC,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;YACpC,OAAO,EAAE,eAAe;YACxB,IAAI,EAAE;gBACL,MAAM;aACN;SACD,CAAC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,QAAQ,CAAI,IAAO;QAC/B,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;YACpC,OAAO,EAAE,UAAU;SACnB,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,MAAM,IAAI,YAAY,CAAC,iBAAiB,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,WAAW,CAAC,QAAQ,CAChD,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAC1B,eAAe,CAAC,IAAI,CACpB,CAAC;QAEF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CACvD,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,2BAA2B,EAAE,EACrD,mBAAmB,CAAC,gBAAgB,EACpC,cAAc,CACd,CAAC;QAEF,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAEnE,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;gBACpC,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE;oBACL,MAAM;iBACN;aACD,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,iBAAiB,CAAC,UAAU;gBACpC,OAAO,EAAE,gBAAgB;gBACzB,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;aACjC,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,UAAU,CAAC,MAAc;QACrC,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;YACpC,OAAO,EAAE,YAAY;YACrB,IAAI,EAAE;gBACL,MAAM;aACN;SACD,CAAC,CAAC;QAEH,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAEhD,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;gBACpC,OAAO,EAAE,aAAa;gBACtB,IAAI,EAAE;oBACL,MAAM;iBACN;aACD,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;gBACxB,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,iBAAiB,CAAC,UAAU;gBACpC,OAAO,EAAE,kBAAkB;gBAC3B,IAAI,EAAE;oBACL,MAAM;iBACN;gBACD,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;aACjC,CAAC,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YACxB,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,iBAAiB,CAAC,UAAU;YACpC,OAAO,EAAE,iBAAiB;YAC1B,IAAI,EAAE;gBACL,MAAM;aACN;SACD,CAAC,CAAC;IACJ,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IBlobStorageConnector } from \"@twin.org/blob-storage-models\";\nimport {\n\tBaseError,\n\tCompression,\n\tCompressionType,\n\tGeneralError,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type IVaultConnector, VaultEncryptionType } from \"@twin.org/vault-models\";\n\n/**\n * Class for performing blob storage operations.\n */\nexport class BlobStorageHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<BlobStorageHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The vault connector.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The blob storage connector to use.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageConnector: IBlobStorageConnector;\n\n\t/**\n\t * The id of the vault key to use for encrypting/decrypting blobs.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageEncryptionKeyId: string;\n\n\t/**\n\t * Is this a trusted node.\n\t * @internal\n\t */\n\tprivate readonly _isTrustedNode: boolean;\n\n\t/**\n\t * The node id of this node.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of BlobStorageHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param vaultConnector The vault connector to use for for the encryption key.\n\t * @param blobStorageConnector The blob storage component to use.\n\t * @param blobStorageEncryptionKeyId The id of the vault key to use for encrypting/decrypting blobs.\n\t * @param isTrustedNode Is this a trusted node.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\tvaultConnector: IVaultConnector,\n\t\tblobStorageConnector: IBlobStorageConnector,\n\t\tblobStorageEncryptionKeyId: string,\n\t\tisTrustedNode: boolean\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._vaultConnector = vaultConnector;\n\t\tthis._blobStorageConnector = blobStorageConnector;\n\t\tthis._blobStorageEncryptionKeyId = blobStorageEncryptionKeyId;\n\t\tthis._isTrustedNode = isTrustedNode;\n\t}\n\n\t/**\n\t * Set the node id of this node.\n\t * @param nodeId The node id to set.\n\t */\n\tpublic setNodeId(nodeId: string): void {\n\t\tthis._nodeId = nodeId;\n\t}\n\n\t/**\n\t * Load a blob from storage.\n\t * @param blobId The id of the blob to apply.\n\t * @returns The blob.\n\t */\n\tpublic async loadBlob<T>(blobId: string): Promise<T | undefined> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"loadBlob\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tconst encryptedBlob = await this._blobStorageConnector.get(blobId);\n\n\t\t\tif (Is.uint8Array(encryptedBlob)) {\n\t\t\t\tconst compressedBlob = await this._vaultConnector.decrypt(\n\t\t\t\t\t`${this._nodeId}/${this._blobStorageEncryptionKeyId}`,\n\t\t\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\t\t\tencryptedBlob\n\t\t\t\t);\n\n\t\t\t\tconst decompressedBlob = await Compression.decompress(compressedBlob, CompressionType.Gzip);\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"loadedBlob\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tblobId\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn ObjectHelper.fromBytes<T>(decompressedBlob);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"loadBlobFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"loadBlobEmpty\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Save a blob.\n\t * @param blob The blob to save.\n\t * @returns The id of the blob.\n\t */\n\tpublic async saveBlob<T>(blob: T): Promise<string> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"saveBlob\"\n\t\t});\n\n\t\tif (!this._isTrustedNode) {\n\t\t\tthrow new GeneralError(BlobStorageHelper.CLASS_NAME, \"notTrustedNode\");\n\t\t}\n\n\t\tconst compressedBlob = await Compression.compress(\n\t\t\tObjectHelper.toBytes(blob),\n\t\t\tCompressionType.Gzip\n\t\t);\n\n\t\tconst encryptedBlob = await this._vaultConnector.encrypt(\n\t\t\t`${this._nodeId}/${this._blobStorageEncryptionKeyId}`,\n\t\t\tVaultEncryptionType.ChaCha20Poly1305,\n\t\t\tcompressedBlob\n\t\t);\n\n\t\ttry {\n\t\t\tconst blobId = await this._blobStorageConnector.set(encryptedBlob);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"savedBlob\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn blobId;\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"saveBlobFailed\",\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Remove a blob from storage.\n\t * @param blobId The id of the blob to remove.\n\t * @returns A promise that resolves when the blob is removed.\n\t */\n\tpublic async removeBlob(blobId: string): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"removeBlob\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tawait this._blobStorageConnector.remove(blobId);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"removedBlob\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\t\tmessage: \"removeBlobFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tblobId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: BlobStorageHelper.CLASS_NAME,\n\t\t\tmessage: \"removeBlobEmpty\",\n\t\t\tdata: {\n\t\t\t\tblobId\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
|
|
@@ -103,7 +103,7 @@ export class ChangeSetHelper {
|
|
|
103
103
|
/**
|
|
104
104
|
* Apply a sync changeset.
|
|
105
105
|
* @param syncChangeset The sync changeset to apply.
|
|
106
|
-
* @returns
|
|
106
|
+
* @returns A promise that resolves when all changes in the set have been published to the event bus.
|
|
107
107
|
*/
|
|
108
108
|
async applyChangeset(syncChangeset) {
|
|
109
109
|
if (Is.arrayValue(syncChangeset.changes)) {
|
|
@@ -192,8 +192,8 @@ export class ChangeSetHelper {
|
|
|
192
192
|
/**
|
|
193
193
|
* Reset the storage for a given storage key.
|
|
194
194
|
* @param storageKey The key of the storage to reset.
|
|
195
|
-
* @param resetMode The reset mode,
|
|
196
|
-
* @returns
|
|
195
|
+
* @param resetMode The reset mode, which uses the nodeId in the entities to determine which are local or remote.
|
|
196
|
+
* @returns A promise that resolves when the reset event is published to the event bus.
|
|
197
197
|
*/
|
|
198
198
|
async reset(storageKey, resetMode) {
|
|
199
199
|
// If we are applying a consolidation we need to reset the local db
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changeSetHelper.js","sourceRoot":"","sources":["../../../src/helpers/changeSetHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { BaseError, Converter, Is, ObjectHelper, RandomHelper } from \"@twin.org/core\";\nimport type { IEventBusComponent } from \"@twin.org/event-bus-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype ISyncChangeSet,\n\ttype ISyncItemRemove,\n\ttype ISyncItemSet,\n\ttype ISyncReset,\n\tSyncChangeOperation,\n\tSynchronisedStorageTopics,\n\ttype SyncNodeIdMode\n} from \"@twin.org/synchronised-storage-models\";\nimport type { BlobStorageHelper } from \"./blobStorageHelper.js\";\n\n/**\n * Class for performing change set operations.\n */\nexport class ChangeSetHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ChangeSetHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The event bus component.\n\t * @internal\n\t */\n\tprivate readonly _eventBusComponent: IEventBusComponent;\n\n\t/**\n\t * The blob storage helper to use for remote sync states.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageHelper: BlobStorageHelper;\n\n\t/**\n\t * The identity of the node that is performing the update.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of ChangeSetHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param eventBusComponent The event bus component to use for events.\n\t * @param blobStorageHelper The blob storage component to use for remote sync states.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\teventBusComponent: IEventBusComponent,\n\t\tblobStorageHelper: BlobStorageHelper\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._eventBusComponent = eventBusComponent;\n\t\tthis._blobStorageHelper = blobStorageHelper;\n\t}\n\n\t/**\n\t * Set the node identity to use for signing changesets.\n\t * @param nodeId The identity of the node that is performing the update.\n\t */\n\tpublic setNodeId(nodeId: string): void {\n\t\tthis._nodeId = nodeId;\n\t}\n\n\t/**\n\t * Get a changeset.\n\t * @param changeSetStorageId The id of the sync changeset to apply.\n\t * @returns The changeset if it was verified.\n\t */\n\tpublic async getChangeset(changeSetStorageId: string): Promise<ISyncChangeSet | undefined> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"getChangeSet\",\n\t\t\tdata: {\n\t\t\t\tchangeSetStorageId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tconst syncChangeSet =\n\t\t\t\tawait this._blobStorageHelper.loadBlob<ISyncChangeSet>(changeSetStorageId);\n\n\t\t\treturn syncChangeSet;\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\tmessage: \"getChangeSetError\",\n\t\t\t\tdata: {\n\t\t\t\t\tchangeSetStorageId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"getChangeSetEmpty\",\n\t\t\tdata: {\n\t\t\t\tchangeSetStorageId\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Apply a sync changeset.\n\t * @param changeSetStorageId The id of the sync changeset to apply.\n\t * @returns The changeset if it existed.\n\t */\n\tpublic async getAndApplyChangeset(\n\t\tchangeSetStorageId: string\n\t): Promise<ISyncChangeSet | undefined> {\n\t\tconst syncChangeset = await this.getChangeset(changeSetStorageId);\n\n\t\t// Only apply changesets from other nodes, we don't want to overwrite\n\t\t// any changes we have made to local entity storage\n\t\tif (!Is.empty(syncChangeset) && syncChangeset.nodeIdentity !== this._nodeId) {\n\t\t\tawait this.applyChangeset(syncChangeset);\n\t\t}\n\n\t\treturn syncChangeset;\n\t}\n\n\t/**\n\t * Apply a sync changeset.\n\t * @param syncChangeset The sync changeset to apply.\n\t * @returns Nothing.\n\t */\n\tpublic async applyChangeset(syncChangeset: ISyncChangeSet): Promise<void> {\n\t\tif (Is.arrayValue(syncChangeset.changes)) {\n\t\t\tfor (const change of syncChangeset.changes) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"changeSetApplyingChange\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\toperation: change.operation,\n\t\t\t\t\t\tid: change.id\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tswitch (change.operation) {\n\t\t\t\t\tcase SyncChangeOperation.Set:\n\t\t\t\t\t\tif (!Is.empty(change.entity)) {\n\t\t\t\t\t\t\t// The id was stripped from the entity as it is part of the operation\n\t\t\t\t\t\t\t// we make sure we reinstate it in the publish\n\t\t\t\t\t\t\t// Also the node identity was stripped when stored in the changeset\n\t\t\t\t\t\t\t// as the changeset is signed with the node identity.\n\t\t\t\t\t\t\t// so we need to restore it here.\n\t\t\t\t\t\t\tawait this._eventBusComponent.publish<ISyncItemSet>(\n\t\t\t\t\t\t\t\tSynchronisedStorageTopics.RemoteItemSet,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstorageKey: syncChangeset.storageKey,\n\t\t\t\t\t\t\t\t\tentity: {\n\t\t\t\t\t\t\t\t\t\t...change.entity,\n\t\t\t\t\t\t\t\t\t\tid: change.id,\n\t\t\t\t\t\t\t\t\t\tnodeIdentity: syncChangeset.nodeIdentity\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SyncChangeOperation.Delete:\n\t\t\t\t\t\tif (!Is.empty(change.id)) {\n\t\t\t\t\t\t\tawait this._eventBusComponent.publish<ISyncItemRemove>(\n\t\t\t\t\t\t\t\tSynchronisedStorageTopics.RemoteItemRemove,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstorageKey: syncChangeset.storageKey,\n\t\t\t\t\t\t\t\t\tid: change.id,\n\t\t\t\t\t\t\t\t\tnodeId: syncChangeset.nodeIdentity\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Store the changeset.\n\t * @param syncChangeSet The sync change set to store.\n\t * @returns The id of the change set.\n\t */\n\tpublic async storeChangeSet(syncChangeSet: ISyncChangeSet): Promise<string> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"changeSetStoring\",\n\t\t\tdata: {\n\t\t\t\tid: syncChangeSet.id\n\t\t\t}\n\t\t});\n\n\t\treturn this._blobStorageHelper.saveBlob(syncChangeSet);\n\t}\n\n\t/**\n\t * Copy a change set.\n\t * @param syncChangeSet The sync changeset to copy.\n\t * @returns The id of the updated change set.\n\t */\n\tpublic async copyChangeset(syncChangeSet: ISyncChangeSet): Promise<\n\t\t| {\n\t\t\t\tsyncChangeSet: ISyncChangeSet;\n\t\t\t\tchangeSetStorageId: string;\n\t\t }\n\t\t| undefined\n\t> {\n\t\tif (Is.stringValue(this._nodeId)) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\tmessage: \"copyChangeSet\",\n\t\t\t\tdata: {\n\t\t\t\t\tchangeSetStorageId: syncChangeSet.id\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Allocate a new id to the changeset copy\n\t\t\tconst copy = ObjectHelper.clone(syncChangeSet);\n\t\t\tcopy.id = Converter.bytesToHex(RandomHelper.generate(32));\n\n\t\t\t// Store the copy\n\t\t\treturn {\n\t\t\t\tsyncChangeSet: copy,\n\t\t\t\tchangeSetStorageId: await this.storeChangeSet(copy)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Reset the storage for a given storage key.\n\t * @param storageKey The key of the storage to reset.\n\t * @param resetMode The reset mode, this will use the nodeId in the entities to determine which are local/remote.\n\t * @returns Nothing.\n\t */\n\tpublic async reset(storageKey: string, resetMode: SyncNodeIdMode): Promise<void> {\n\t\t// If we are applying a consolidation we need to reset the local db\n\t\t// but keep any entries from the local node, as they might have been updated\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"storageReset\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\t\tawait this._eventBusComponent.publish<ISyncReset>(SynchronisedStorageTopics.Reset, {\n\t\t\tstorageKey,\n\t\t\tresetMode\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"changeSetHelper.js","sourceRoot":"","sources":["../../../src/helpers/changeSetHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { BaseError, Converter, Is, ObjectHelper, RandomHelper } from \"@twin.org/core\";\nimport type { IEventBusComponent } from \"@twin.org/event-bus-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype ISyncChangeSet,\n\ttype ISyncItemRemove,\n\ttype ISyncItemSet,\n\ttype ISyncReset,\n\tSyncChangeOperation,\n\tSynchronisedStorageTopics,\n\ttype SyncNodeIdMode\n} from \"@twin.org/synchronised-storage-models\";\nimport type { BlobStorageHelper } from \"./blobStorageHelper.js\";\n\n/**\n * Class for performing change set operations.\n */\nexport class ChangeSetHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ChangeSetHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The event bus component.\n\t * @internal\n\t */\n\tprivate readonly _eventBusComponent: IEventBusComponent;\n\n\t/**\n\t * The blob storage helper to use for remote sync states.\n\t * @internal\n\t */\n\tprivate readonly _blobStorageHelper: BlobStorageHelper;\n\n\t/**\n\t * The identity of the node that is performing the update.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of ChangeSetHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param eventBusComponent The event bus component to use for events.\n\t * @param blobStorageHelper The blob storage component to use for remote sync states.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\teventBusComponent: IEventBusComponent,\n\t\tblobStorageHelper: BlobStorageHelper\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._eventBusComponent = eventBusComponent;\n\t\tthis._blobStorageHelper = blobStorageHelper;\n\t}\n\n\t/**\n\t * Set the node identity to use for signing changesets.\n\t * @param nodeId The identity of the node that is performing the update.\n\t */\n\tpublic setNodeId(nodeId: string): void {\n\t\tthis._nodeId = nodeId;\n\t}\n\n\t/**\n\t * Get a changeset.\n\t * @param changeSetStorageId The id of the sync changeset to apply.\n\t * @returns The changeset if it was verified.\n\t */\n\tpublic async getChangeset(changeSetStorageId: string): Promise<ISyncChangeSet | undefined> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"getChangeSet\",\n\t\t\tdata: {\n\t\t\t\tchangeSetStorageId\n\t\t\t}\n\t\t});\n\n\t\ttry {\n\t\t\tconst syncChangeSet =\n\t\t\t\tawait this._blobStorageHelper.loadBlob<ISyncChangeSet>(changeSetStorageId);\n\n\t\t\treturn syncChangeSet;\n\t\t} catch (error) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\tmessage: \"getChangeSetError\",\n\t\t\t\tdata: {\n\t\t\t\t\tchangeSetStorageId\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"getChangeSetEmpty\",\n\t\t\tdata: {\n\t\t\t\tchangeSetStorageId\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Apply a sync changeset.\n\t * @param changeSetStorageId The id of the sync changeset to apply.\n\t * @returns The changeset if it existed.\n\t */\n\tpublic async getAndApplyChangeset(\n\t\tchangeSetStorageId: string\n\t): Promise<ISyncChangeSet | undefined> {\n\t\tconst syncChangeset = await this.getChangeset(changeSetStorageId);\n\n\t\t// Only apply changesets from other nodes, we don't want to overwrite\n\t\t// any changes we have made to local entity storage\n\t\tif (!Is.empty(syncChangeset) && syncChangeset.nodeIdentity !== this._nodeId) {\n\t\t\tawait this.applyChangeset(syncChangeset);\n\t\t}\n\n\t\treturn syncChangeset;\n\t}\n\n\t/**\n\t * Apply a sync changeset.\n\t * @param syncChangeset The sync changeset to apply.\n\t * @returns A promise that resolves when all changes in the set have been published to the event bus.\n\t */\n\tpublic async applyChangeset(syncChangeset: ISyncChangeSet): Promise<void> {\n\t\tif (Is.arrayValue(syncChangeset.changes)) {\n\t\t\tfor (const change of syncChangeset.changes) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"changeSetApplyingChange\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\toperation: change.operation,\n\t\t\t\t\t\tid: change.id\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tswitch (change.operation) {\n\t\t\t\t\tcase SyncChangeOperation.Set:\n\t\t\t\t\t\tif (!Is.empty(change.entity)) {\n\t\t\t\t\t\t\t// The id was stripped from the entity as it is part of the operation\n\t\t\t\t\t\t\t// we make sure we reinstate it in the publish\n\t\t\t\t\t\t\t// Also the node identity was stripped when stored in the changeset\n\t\t\t\t\t\t\t// as the changeset is signed with the node identity.\n\t\t\t\t\t\t\t// so we need to restore it here.\n\t\t\t\t\t\t\tawait this._eventBusComponent.publish<ISyncItemSet>(\n\t\t\t\t\t\t\t\tSynchronisedStorageTopics.RemoteItemSet,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstorageKey: syncChangeset.storageKey,\n\t\t\t\t\t\t\t\t\tentity: {\n\t\t\t\t\t\t\t\t\t\t...change.entity,\n\t\t\t\t\t\t\t\t\t\tid: change.id,\n\t\t\t\t\t\t\t\t\t\tnodeIdentity: syncChangeset.nodeIdentity\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SyncChangeOperation.Delete:\n\t\t\t\t\t\tif (!Is.empty(change.id)) {\n\t\t\t\t\t\t\tawait this._eventBusComponent.publish<ISyncItemRemove>(\n\t\t\t\t\t\t\t\tSynchronisedStorageTopics.RemoteItemRemove,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstorageKey: syncChangeset.storageKey,\n\t\t\t\t\t\t\t\t\tid: change.id,\n\t\t\t\t\t\t\t\t\tnodeId: syncChangeset.nodeIdentity\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Store the changeset.\n\t * @param syncChangeSet The sync change set to store.\n\t * @returns The id of the change set.\n\t */\n\tpublic async storeChangeSet(syncChangeSet: ISyncChangeSet): Promise<string> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"changeSetStoring\",\n\t\t\tdata: {\n\t\t\t\tid: syncChangeSet.id\n\t\t\t}\n\t\t});\n\n\t\treturn this._blobStorageHelper.saveBlob(syncChangeSet);\n\t}\n\n\t/**\n\t * Copy a change set.\n\t * @param syncChangeSet The sync changeset to copy.\n\t * @returns The id of the updated change set.\n\t */\n\tpublic async copyChangeset(syncChangeSet: ISyncChangeSet): Promise<\n\t\t| {\n\t\t\t\tsyncChangeSet: ISyncChangeSet;\n\t\t\t\tchangeSetStorageId: string;\n\t\t }\n\t\t| undefined\n\t> {\n\t\tif (Is.stringValue(this._nodeId)) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\t\tmessage: \"copyChangeSet\",\n\t\t\t\tdata: {\n\t\t\t\t\tchangeSetStorageId: syncChangeSet.id\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Allocate a new id to the changeset copy\n\t\t\tconst copy = ObjectHelper.clone(syncChangeSet);\n\t\t\tcopy.id = Converter.bytesToHex(RandomHelper.generate(32));\n\n\t\t\t// Store the copy\n\t\t\treturn {\n\t\t\t\tsyncChangeSet: copy,\n\t\t\t\tchangeSetStorageId: await this.storeChangeSet(copy)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Reset the storage for a given storage key.\n\t * @param storageKey The key of the storage to reset.\n\t * @param resetMode The reset mode, which uses the nodeId in the entities to determine which are local or remote.\n\t * @returns A promise that resolves when the reset event is published to the event bus.\n\t */\n\tpublic async reset(storageKey: string, resetMode: SyncNodeIdMode): Promise<void> {\n\t\t// If we are applying a consolidation we need to reset the local db\n\t\t// but keep any entries from the local node, as they might have been updated\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: ChangeSetHelper.CLASS_NAME,\n\t\t\tmessage: \"storageReset\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\t\tawait this._eventBusComponent.publish<ISyncReset>(SynchronisedStorageTopics.Reset, {\n\t\t\tstorageKey,\n\t\t\tresetMode\n\t\t});\n\t}\n}\n"]}
|
|
@@ -43,7 +43,7 @@ export class LocalSyncStateHelper {
|
|
|
43
43
|
* @param storageKey The storage key of the snapshot to add the change for.
|
|
44
44
|
* @param operation The operation to perform.
|
|
45
45
|
* @param id The id of the entity to add the change for.
|
|
46
|
-
* @returns
|
|
46
|
+
* @returns A promise that resolves when the change is recorded in the local snapshot.
|
|
47
47
|
*/
|
|
48
48
|
async addLocalChange(storageKey, operation, id) {
|
|
49
49
|
await this._logging?.log({
|
|
@@ -143,7 +143,7 @@ export class LocalSyncStateHelper {
|
|
|
143
143
|
/**
|
|
144
144
|
* Set the current local snapshot with changes for this node.
|
|
145
145
|
* @param localChangeSnapshot The local change snapshot to set.
|
|
146
|
-
* @returns
|
|
146
|
+
* @returns A promise that resolves when the snapshot is persisted to entity storage.
|
|
147
147
|
*/
|
|
148
148
|
async setLocalChangeSnapshot(localChangeSnapshot) {
|
|
149
149
|
await this._logging?.log({
|
|
@@ -157,9 +157,9 @@ export class LocalSyncStateHelper {
|
|
|
157
157
|
await this._snapshotEntryEntityStorage.set(localChangeSnapshot);
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
|
-
*
|
|
160
|
+
* Remove the local snapshot entry from storage.
|
|
161
161
|
* @param localChangeSnapshot The local change snapshot to remove.
|
|
162
|
-
* @returns
|
|
162
|
+
* @returns A promise that resolves when the snapshot is removed from entity storage.
|
|
163
163
|
*/
|
|
164
164
|
async removeLocalChangeSnapshot(localChangeSnapshot) {
|
|
165
165
|
await this._logging?.log({
|
|
@@ -176,7 +176,7 @@ export class LocalSyncStateHelper {
|
|
|
176
176
|
* Apply a sync state to the local node.
|
|
177
177
|
* @param storageKey The storage key of the snapshot to sync with.
|
|
178
178
|
* @param syncState The sync state to sync with.
|
|
179
|
-
* @returns
|
|
179
|
+
* @returns A promise that resolves when all new and modified snapshots have been processed.
|
|
180
180
|
*/
|
|
181
181
|
async applySyncState(storageKey, syncState) {
|
|
182
182
|
await this._logging?.log({
|
|
@@ -324,7 +324,7 @@ export class LocalSyncStateHelper {
|
|
|
324
324
|
/**
|
|
325
325
|
* Process the modified snapshots and store them in the local storage.
|
|
326
326
|
* @param modifiedSnapshots The modified snapshots to process.
|
|
327
|
-
* @returns
|
|
327
|
+
* @returns A promise that resolves when all new changesets in each snapshot have been applied and the snapshots saved.
|
|
328
328
|
* @internal
|
|
329
329
|
*/
|
|
330
330
|
async processModifiedSnapshots(modifiedSnapshots) {
|
|
@@ -357,7 +357,7 @@ export class LocalSyncStateHelper {
|
|
|
357
357
|
/**
|
|
358
358
|
* Process the new snapshots and store them in the local storage.
|
|
359
359
|
* @param newSnapshots The new snapshots to process.
|
|
360
|
-
* @returns
|
|
360
|
+
* @returns A promise that resolves when all changesets for each snapshot have been applied and the snapshots saved.
|
|
361
361
|
* @internal
|
|
362
362
|
*/
|
|
363
363
|
async processNewSnapshots(newSnapshots) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localSyncStateHelper.js","sourceRoot":"","sources":["../../../src/helpers/localSyncStateHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, Is, RandomHelper } from \"@twin.org/core\";\nimport { ComparisonOperator } from \"@twin.org/entity\";\nimport type { IEntityStorageConnector } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type SyncChangeOperation, SyncNodeIdMode } from \"@twin.org/synchronised-storage-models\";\nimport type { ChangeSetHelper } from \"./changeSetHelper.js\";\nimport { SYNC_SNAPSHOT_VERSION } from \"./versions.js\";\nimport type { SyncSnapshotEntry } from \"../entities/syncSnapshotEntry.js\";\nimport type { ISyncState } from \"../models/ISyncState.js\";\n\n/**\n * Class for performing entity storage operations in decentralised storage.\n */\nexport class LocalSyncStateHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<LocalSyncStateHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The storage connector for the sync snapshot entries.\n\t * @internal\n\t */\n\tprivate readonly _snapshotEntryEntityStorage: IEntityStorageConnector<SyncSnapshotEntry>;\n\n\t/**\n\t * The change set helper to use for applying changesets.\n\t * @internal\n\t */\n\tprivate readonly _changeSetHelper: ChangeSetHelper;\n\n\t/**\n\t * Create a new instance of LocalSyncStateHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param snapshotEntryEntityStorage The storage connector for the sync snapshot entries.\n\t * @param changeSetHelper The change set helper to use for applying changesets.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\tsnapshotEntryEntityStorage: IEntityStorageConnector<SyncSnapshotEntry>,\n\t\tchangeSetHelper: ChangeSetHelper\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._snapshotEntryEntityStorage = snapshotEntryEntityStorage;\n\t\tthis._changeSetHelper = changeSetHelper;\n\t}\n\n\t/**\n\t * Add a new change to the local snapshot.\n\t * @param storageKey The storage key of the snapshot to add the change for.\n\t * @param operation The operation to perform.\n\t * @param id The id of the entity to add the change for.\n\t * @returns Nothing.\n\t */\n\tpublic async addLocalChange(\n\t\tstorageKey: string,\n\t\toperation: SyncChangeOperation,\n\t\tid: string\n\t): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"addLocalChange\",\n\t\t\tdata: {\n\t\t\t\tstorageKey,\n\t\t\t\toperation,\n\t\t\t\tid\n\t\t\t}\n\t\t});\n\n\t\tconst localChangeSnapshots = await this.getSnapshots(storageKey, true);\n\n\t\tif (localChangeSnapshots.length > 0) {\n\t\t\tconst localChangeSnapshot = localChangeSnapshots[0];\n\n\t\t\tlocalChangeSnapshot.changes ??= [];\n\n\t\t\t// If we already have a change for this id we are\n\t\t\t// about to supersede it, we remove the previous change\n\t\t\t// to avoid having multiple changes for the same id\n\t\t\tconst previousChangeIndex = localChangeSnapshot.changes.findIndex(change => change.id === id);\n\t\t\tif (previousChangeIndex !== -1) {\n\t\t\t\tlocalChangeSnapshot.changes.splice(previousChangeIndex, 1);\n\t\t\t}\n\n\t\t\t// If we already have changes from previous updates\n\t\t\t// then make sure we update the dateModified, otherwise\n\t\t\t// we assume this is the first change and setting modified is not necessary\n\t\t\tif (localChangeSnapshot.changes.length > 0) {\n\t\t\t\tlocalChangeSnapshot.dateModified = new Date(Date.now()).toISOString();\n\t\t\t}\n\n\t\t\tlocalChangeSnapshot.changes.push({ operation, id });\n\n\t\t\tawait this.setLocalChangeSnapshot(localChangeSnapshot);\n\t\t}\n\t}\n\n\t/**\n\t * Get the snapshot which contains just the changes for this node.\n\t * @param storageKey The storage key of the snapshot to get.\n\t * @param isLocal Whether to get the local snapshot or not.\n\t * @returns The local snapshot entry.\n\t */\n\tpublic async getSnapshots(storageKey: string, isLocal: boolean): Promise<SyncSnapshotEntry[]> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"getSnapshots\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\n\t\tconst queryResult = await this._snapshotEntryEntityStorage.query({\n\t\t\tconditions: [\n\t\t\t\t{\n\t\t\t\t\tproperty: \"isLocal\",\n\t\t\t\t\tvalue: isLocal,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproperty: \"storageKey\",\n\t\t\t\t\tvalue: storageKey,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals\n\t\t\t\t}\n\t\t\t]\n\t\t});\n\n\t\tif (queryResult.entities.length > 0) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"getSnapshotsExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn queryResult.entities as SyncSnapshotEntry[];\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"getSnapshotsDoesNotExist\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\t\tconst now = new Date(Date.now()).toISOString();\n\t\treturn [\n\t\t\t{\n\t\t\t\tversion: SYNC_SNAPSHOT_VERSION,\n\t\t\t\tid: Converter.bytesToHex(RandomHelper.generate(32)),\n\t\t\t\tstorageKey,\n\t\t\t\tdateCreated: now,\n\t\t\t\tdateModified: now,\n\t\t\t\tchangeSetStorageIds: [],\n\t\t\t\tisLocal,\n\t\t\t\tisConsolidated: false,\n\t\t\t\tepoch: 0\n\t\t\t}\n\t\t];\n\t}\n\n\t/**\n\t * Set the current local snapshot with changes for this node.\n\t * @param localChangeSnapshot The local change snapshot to set.\n\t * @returns Nothing.\n\t */\n\tpublic async setLocalChangeSnapshot(localChangeSnapshot: SyncSnapshotEntry): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"setLocalChangeSnapshot\",\n\t\t\tdata: {\n\t\t\t\tstorageKey: localChangeSnapshot.storageKey\n\t\t\t}\n\t\t});\n\t\tawait this._snapshotEntryEntityStorage.set(localChangeSnapshot);\n\t}\n\n\t/**\n\t * Get the current local snapshot with the changes for this node.\n\t * @param localChangeSnapshot The local change snapshot to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async removeLocalChangeSnapshot(localChangeSnapshot: SyncSnapshotEntry): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"removeLocalChangeSnapshot\",\n\t\t\tdata: {\n\t\t\t\tsnapshotId: localChangeSnapshot.id\n\t\t\t}\n\t\t});\n\t\tawait this._snapshotEntryEntityStorage.remove(localChangeSnapshot.id);\n\t}\n\n\t/**\n\t * Apply a sync state to the local node.\n\t * @param storageKey The storage key of the snapshot to sync with.\n\t * @param syncState The sync state to sync with.\n\t * @returns Nothing.\n\t */\n\tpublic async applySyncState(storageKey: string, syncState: ISyncState): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"applySyncState\",\n\t\t\tdata: {\n\t\t\t\tsnapshotCount: syncState.snapshots.length\n\t\t\t}\n\t\t});\n\n\t\t// Get all the existing snapshots that we have processed previously\n\t\tlet existingSnapshots = await this.getSnapshots(storageKey, false);\n\n\t\t// Sort from newest to oldest\n\t\texistingSnapshots = existingSnapshots.sort(\n\t\t\t(a, b) => new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime()\n\t\t);\n\n\t\t// Sort from newest to oldest\n\t\tconst syncStateSnapshots = syncState.snapshots.sort(\n\t\t\t(a, b) => new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime()\n\t\t);\n\n\t\t// Get the newest epoch from the local storage\n\t\tconst newestExistingEpoch = existingSnapshots[0]?.epoch ?? 0;\n\n\t\t// Get the oldest epoch from the remote storage\n\t\tconst oldestSyncStateEpoch = syncStateSnapshots[syncStateSnapshots.length - 1]?.epoch ?? 0;\n\n\t\t// If there is a gap between the largest epoch we have locally\n\t\t// and the smallest epoch we have remotely then we have missed\n\t\t// data so we need to perform a full sync\n\t\tconst hasEpochGap = newestExistingEpoch + 1 < oldestSyncStateEpoch;\n\n\t\t// If we have an epoch gap or no existing snapshots then we need to apply\n\t\t// a full sync from a consolidation\n\t\tif (!existingSnapshots.some(s => s.isConsolidated) || hasEpochGap) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"applySnapshotNoExisting\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst mostRecentConsolidation = syncStateSnapshots.findIndex(\n\t\t\t\tsnapshot => snapshot.isConsolidated\n\t\t\t);\n\t\t\tif (mostRecentConsolidation !== -1) {\n\t\t\t\t// We found the most recent consolidated snapshot, we can use it\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshotFoundConsolidated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\tsnapshotId: syncStateSnapshots[mostRecentConsolidation].id\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// We need to reset the entity storage and remove all the remote items\n\t\t\t\t// so that we use just the ones from the consolidation, since\n\t\t\t\t// we don't have any existing there shouldn't be any remote entries\n\t\t\t\t// but we reset nonetheless\n\t\t\t\tawait this._changeSetHelper.reset(storageKey, SyncNodeIdMode.Remote);\n\n\t\t\t\t// We need to process the most recent consolidation and all changes\n\t\t\t\t// that were made since then, from newest to oldest (so newer changes override older ones)\n\t\t\t\t// Process snapshots from the consolidation point (most recent) back to the newest\n\t\t\t\tfor (let i = mostRecentConsolidation; i >= 0; i--) {\n\t\t\t\t\tawait this.processNewSnapshots([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...syncStateSnapshots[i],\n\t\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\t\tisLocal: false\n\t\t\t\t\t\t}\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshotNoConsolidated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tstorageKey\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\t// We have existing consolidated remote snapshots, so we can assume that we have\n\t\t\t// applied at least one consolidation snapshot, in this case we need to look at the changes since\n\t\t\t// then and apply them if we haven't already\n\t\t\t// We don't need to apply any additional consolidated snapshots, just the changesets\n\n\t\t\t// Create a lookup map for the existing snapshots\n\t\t\tconst existingSnapshotsMap: { [id: string]: SyncSnapshotEntry } = {};\n\t\t\tfor (const snapshot of existingSnapshots) {\n\t\t\t\texistingSnapshotsMap[snapshot.id] = snapshot;\n\t\t\t}\n\n\t\t\tconst newSnapshots: SyncSnapshotEntry[] = [];\n\t\t\tconst modifiedSnapshots: {\n\t\t\t\tcurrentSnapshot: SyncSnapshotEntry;\n\t\t\t\tupdatedSnapshot: SyncSnapshotEntry;\n\t\t\t}[] = [];\n\t\t\tconst referencedExistingSnapshots: string[] = Object.keys(existingSnapshotsMap);\n\n\t\t\tlet completedProcessing = false;\n\t\t\tfor (const snapshot of syncStateSnapshots) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshot\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tsnapshotId: snapshot.id,\n\t\t\t\t\t\tdateCreated: new Date(snapshot.dateCreated).toISOString()\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// See if we have the snapshot stored locally\n\t\t\t\tconst currentSnapshot = existingSnapshotsMap[snapshot.id];\n\n\t\t\t\t// As we are referencing an existing snapshot, we need to remove it from the list\n\t\t\t\t// to allow us to cleanup any unreferenced snapshots later\n\t\t\t\tconst idx = referencedExistingSnapshots.indexOf(snapshot.id);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\treferencedExistingSnapshots.splice(idx, 1);\n\t\t\t\t}\n\n\t\t\t\t// No need to apply consolidated snapshots\n\t\t\t\tif (!snapshot.isConsolidated && !completedProcessing) {\n\t\t\t\t\tconst updatedSnapshot: SyncSnapshotEntry = {\n\t\t\t\t\t\t...snapshot,\n\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\tisLocal: false\n\t\t\t\t\t};\n\n\t\t\t\t\tif (Is.empty(currentSnapshot)) {\n\t\t\t\t\t\t// We don't have the snapshot locally, so we need to process all of it\n\t\t\t\t\t\tnewSnapshots.push(updatedSnapshot);\n\t\t\t\t\t} else if (currentSnapshot.dateModified !== snapshot.dateModified) {\n\t\t\t\t\t\t// If the local snapshot has a different dateModified, we need to update it\n\t\t\t\t\t\tmodifiedSnapshots.push({\n\t\t\t\t\t\t\tcurrentSnapshot,\n\t\t\t\t\t\t\tupdatedSnapshot\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we sorted the snapshots from newest to oldest, so if we found a local snapshot\n\t\t\t\t\t\t// with the same dateModified as the remote snapshot, we can stop processing further\n\t\t\t\t\t\tcompletedProcessing = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We reverse the order of the snapshots to process them from oldest to newest\n\t\t\t// because we want to apply the changes in the order they were created\n\t\t\tawait this.processModifiedSnapshots(modifiedSnapshots.reverse());\n\t\t\tawait this.processNewSnapshots(newSnapshots.reverse());\n\n\t\t\t// Any ids remaining in this list are no longer referenced in the global state\n\t\t\t// so we should remove them from the local storage as they will never be updated again\n\t\t\tfor (const referencedSnapshotId of referencedExistingSnapshots) {\n\t\t\t\tawait this._snapshotEntryEntityStorage.remove(referencedSnapshotId);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Process the modified snapshots and store them in the local storage.\n\t * @param modifiedSnapshots The modified snapshots to process.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async processModifiedSnapshots(\n\t\tmodifiedSnapshots: {\n\t\t\tcurrentSnapshot: SyncSnapshotEntry;\n\t\t\tupdatedSnapshot: SyncSnapshotEntry;\n\t\t}[]\n\t): Promise<void> {\n\t\tfor (const modifiedSnapshot of modifiedSnapshots) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"processModifiedSnapshot\",\n\t\t\t\tdata: {\n\t\t\t\t\tsnapshotId: modifiedSnapshot.updatedSnapshot.id,\n\t\t\t\t\tlocalModified: new Date(\n\t\t\t\t\t\tmodifiedSnapshot.currentSnapshot.dateModified ??\n\t\t\t\t\t\t\tmodifiedSnapshot.currentSnapshot.dateCreated\n\t\t\t\t\t).toISOString(),\n\t\t\t\t\tremoteModified: new Date(\n\t\t\t\t\t\tmodifiedSnapshot.updatedSnapshot.dateModified ??\n\t\t\t\t\t\t\tmodifiedSnapshot.updatedSnapshot.dateCreated\n\t\t\t\t\t).toISOString()\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst remoteChangeSetStorageIds = modifiedSnapshot.updatedSnapshot.changeSetStorageIds;\n\t\t\tconst localChangeSetStorageIds = modifiedSnapshot.currentSnapshot.changeSetStorageIds ?? [];\n\t\t\tif (Is.arrayValue(remoteChangeSetStorageIds)) {\n\t\t\t\tfor (const storageId of remoteChangeSetStorageIds) {\n\t\t\t\t\t// Check if the local snapshot does not have the storageId\n\t\t\t\t\tif (!localChangeSetStorageIds.includes(storageId)) {\n\t\t\t\t\t\tawait this._changeSetHelper.getAndApplyChangeset(storageId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this._snapshotEntryEntityStorage.set(modifiedSnapshot.updatedSnapshot);\n\t\t}\n\t}\n\n\t/**\n\t * Process the new snapshots and store them in the local storage.\n\t * @param newSnapshots The new snapshots to process.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async processNewSnapshots(newSnapshots: SyncSnapshotEntry[]): Promise<void> {\n\t\tfor (const newSnapshot of newSnapshots) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"processNewSnapshot\",\n\t\t\t\tdata: {\n\t\t\t\t\tsnapshotId: newSnapshot.id,\n\t\t\t\t\tdateCreated: newSnapshot.dateCreated\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst newSnapshotChangeSetStorageIds = newSnapshot.changeSetStorageIds ?? [];\n\t\t\tif (Is.arrayValue(newSnapshotChangeSetStorageIds)) {\n\t\t\t\tfor (const storageId of newSnapshotChangeSetStorageIds) {\n\t\t\t\t\tawait this._changeSetHelper.getAndApplyChangeset(storageId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this._snapshotEntryEntityStorage.set(newSnapshot);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"localSyncStateHelper.js","sourceRoot":"","sources":["../../../src/helpers/localSyncStateHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Converter, Is, RandomHelper } from \"@twin.org/core\";\nimport { ComparisonOperator } from \"@twin.org/entity\";\nimport type { IEntityStorageConnector } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type SyncChangeOperation, SyncNodeIdMode } from \"@twin.org/synchronised-storage-models\";\nimport type { ChangeSetHelper } from \"./changeSetHelper.js\";\nimport { SYNC_SNAPSHOT_VERSION } from \"./versions.js\";\nimport type { SyncSnapshotEntry } from \"../entities/syncSnapshotEntry.js\";\nimport type { ISyncState } from \"../models/ISyncState.js\";\n\n/**\n * Class for performing entity storage operations in decentralised storage.\n */\nexport class LocalSyncStateHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<LocalSyncStateHelper>();\n\n\t/**\n\t * The logging component to use for logging.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The storage connector for the sync snapshot entries.\n\t * @internal\n\t */\n\tprivate readonly _snapshotEntryEntityStorage: IEntityStorageConnector<SyncSnapshotEntry>;\n\n\t/**\n\t * The change set helper to use for applying changesets.\n\t * @internal\n\t */\n\tprivate readonly _changeSetHelper: ChangeSetHelper;\n\n\t/**\n\t * Create a new instance of LocalSyncStateHelper.\n\t * @param logging The logging component to use for logging.\n\t * @param snapshotEntryEntityStorage The storage connector for the sync snapshot entries.\n\t * @param changeSetHelper The change set helper to use for applying changesets.\n\t */\n\tconstructor(\n\t\tlogging: ILoggingComponent | undefined,\n\t\tsnapshotEntryEntityStorage: IEntityStorageConnector<SyncSnapshotEntry>,\n\t\tchangeSetHelper: ChangeSetHelper\n\t) {\n\t\tthis._logging = logging;\n\t\tthis._snapshotEntryEntityStorage = snapshotEntryEntityStorage;\n\t\tthis._changeSetHelper = changeSetHelper;\n\t}\n\n\t/**\n\t * Add a new change to the local snapshot.\n\t * @param storageKey The storage key of the snapshot to add the change for.\n\t * @param operation The operation to perform.\n\t * @param id The id of the entity to add the change for.\n\t * @returns A promise that resolves when the change is recorded in the local snapshot.\n\t */\n\tpublic async addLocalChange(\n\t\tstorageKey: string,\n\t\toperation: SyncChangeOperation,\n\t\tid: string\n\t): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"addLocalChange\",\n\t\t\tdata: {\n\t\t\t\tstorageKey,\n\t\t\t\toperation,\n\t\t\t\tid\n\t\t\t}\n\t\t});\n\n\t\tconst localChangeSnapshots = await this.getSnapshots(storageKey, true);\n\n\t\tif (localChangeSnapshots.length > 0) {\n\t\t\tconst localChangeSnapshot = localChangeSnapshots[0];\n\n\t\t\tlocalChangeSnapshot.changes ??= [];\n\n\t\t\t// If we already have a change for this id we are\n\t\t\t// about to supersede it, we remove the previous change\n\t\t\t// to avoid having multiple changes for the same id\n\t\t\tconst previousChangeIndex = localChangeSnapshot.changes.findIndex(change => change.id === id);\n\t\t\tif (previousChangeIndex !== -1) {\n\t\t\t\tlocalChangeSnapshot.changes.splice(previousChangeIndex, 1);\n\t\t\t}\n\n\t\t\t// If we already have changes from previous updates\n\t\t\t// then make sure we update the dateModified, otherwise\n\t\t\t// we assume this is the first change and setting modified is not necessary\n\t\t\tif (localChangeSnapshot.changes.length > 0) {\n\t\t\t\tlocalChangeSnapshot.dateModified = new Date(Date.now()).toISOString();\n\t\t\t}\n\n\t\t\tlocalChangeSnapshot.changes.push({ operation, id });\n\n\t\t\tawait this.setLocalChangeSnapshot(localChangeSnapshot);\n\t\t}\n\t}\n\n\t/**\n\t * Get the snapshot which contains just the changes for this node.\n\t * @param storageKey The storage key of the snapshot to get.\n\t * @param isLocal Whether to get the local snapshot or not.\n\t * @returns The local snapshot entry.\n\t */\n\tpublic async getSnapshots(storageKey: string, isLocal: boolean): Promise<SyncSnapshotEntry[]> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"getSnapshots\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\n\t\tconst queryResult = await this._snapshotEntryEntityStorage.query({\n\t\t\tconditions: [\n\t\t\t\t{\n\t\t\t\t\tproperty: \"isLocal\",\n\t\t\t\t\tvalue: isLocal,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tproperty: \"storageKey\",\n\t\t\t\t\tvalue: storageKey,\n\t\t\t\t\tcomparison: ComparisonOperator.Equals\n\t\t\t\t}\n\t\t\t]\n\t\t});\n\n\t\tif (queryResult.entities.length > 0) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"getSnapshotsExists\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn queryResult.entities as SyncSnapshotEntry[];\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"getSnapshotsDoesNotExist\",\n\t\t\tdata: {\n\t\t\t\tstorageKey\n\t\t\t}\n\t\t});\n\t\tconst now = new Date(Date.now()).toISOString();\n\t\treturn [\n\t\t\t{\n\t\t\t\tversion: SYNC_SNAPSHOT_VERSION,\n\t\t\t\tid: Converter.bytesToHex(RandomHelper.generate(32)),\n\t\t\t\tstorageKey,\n\t\t\t\tdateCreated: now,\n\t\t\t\tdateModified: now,\n\t\t\t\tchangeSetStorageIds: [],\n\t\t\t\tisLocal,\n\t\t\t\tisConsolidated: false,\n\t\t\t\tepoch: 0\n\t\t\t}\n\t\t];\n\t}\n\n\t/**\n\t * Set the current local snapshot with changes for this node.\n\t * @param localChangeSnapshot The local change snapshot to set.\n\t * @returns A promise that resolves when the snapshot is persisted to entity storage.\n\t */\n\tpublic async setLocalChangeSnapshot(localChangeSnapshot: SyncSnapshotEntry): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"setLocalChangeSnapshot\",\n\t\t\tdata: {\n\t\t\t\tstorageKey: localChangeSnapshot.storageKey\n\t\t\t}\n\t\t});\n\t\tawait this._snapshotEntryEntityStorage.set(localChangeSnapshot);\n\t}\n\n\t/**\n\t * Remove the local snapshot entry from storage.\n\t * @param localChangeSnapshot The local change snapshot to remove.\n\t * @returns A promise that resolves when the snapshot is removed from entity storage.\n\t */\n\tpublic async removeLocalChangeSnapshot(localChangeSnapshot: SyncSnapshotEntry): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"removeLocalChangeSnapshot\",\n\t\t\tdata: {\n\t\t\t\tsnapshotId: localChangeSnapshot.id\n\t\t\t}\n\t\t});\n\t\tawait this._snapshotEntryEntityStorage.remove(localChangeSnapshot.id);\n\t}\n\n\t/**\n\t * Apply a sync state to the local node.\n\t * @param storageKey The storage key of the snapshot to sync with.\n\t * @param syncState The sync state to sync with.\n\t * @returns A promise that resolves when all new and modified snapshots have been processed.\n\t */\n\tpublic async applySyncState(storageKey: string, syncState: ISyncState): Promise<void> {\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\tmessage: \"applySyncState\",\n\t\t\tdata: {\n\t\t\t\tsnapshotCount: syncState.snapshots.length\n\t\t\t}\n\t\t});\n\n\t\t// Get all the existing snapshots that we have processed previously\n\t\tlet existingSnapshots = await this.getSnapshots(storageKey, false);\n\n\t\t// Sort from newest to oldest\n\t\texistingSnapshots = existingSnapshots.sort(\n\t\t\t(a, b) => new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime()\n\t\t);\n\n\t\t// Sort from newest to oldest\n\t\tconst syncStateSnapshots = syncState.snapshots.sort(\n\t\t\t(a, b) => new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime()\n\t\t);\n\n\t\t// Get the newest epoch from the local storage\n\t\tconst newestExistingEpoch = existingSnapshots[0]?.epoch ?? 0;\n\n\t\t// Get the oldest epoch from the remote storage\n\t\tconst oldestSyncStateEpoch = syncStateSnapshots[syncStateSnapshots.length - 1]?.epoch ?? 0;\n\n\t\t// If there is a gap between the largest epoch we have locally\n\t\t// and the smallest epoch we have remotely then we have missed\n\t\t// data so we need to perform a full sync\n\t\tconst hasEpochGap = newestExistingEpoch + 1 < oldestSyncStateEpoch;\n\n\t\t// If we have an epoch gap or no existing snapshots then we need to apply\n\t\t// a full sync from a consolidation\n\t\tif (!existingSnapshots.some(s => s.isConsolidated) || hasEpochGap) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"applySnapshotNoExisting\",\n\t\t\t\tdata: {\n\t\t\t\t\tstorageKey\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst mostRecentConsolidation = syncStateSnapshots.findIndex(\n\t\t\t\tsnapshot => snapshot.isConsolidated\n\t\t\t);\n\t\t\tif (mostRecentConsolidation !== -1) {\n\t\t\t\t// We found the most recent consolidated snapshot, we can use it\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshotFoundConsolidated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\tsnapshotId: syncStateSnapshots[mostRecentConsolidation].id\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// We need to reset the entity storage and remove all the remote items\n\t\t\t\t// so that we use just the ones from the consolidation, since\n\t\t\t\t// we don't have any existing there shouldn't be any remote entries\n\t\t\t\t// but we reset nonetheless\n\t\t\t\tawait this._changeSetHelper.reset(storageKey, SyncNodeIdMode.Remote);\n\n\t\t\t\t// We need to process the most recent consolidation and all changes\n\t\t\t\t// that were made since then, from newest to oldest (so newer changes override older ones)\n\t\t\t\t// Process snapshots from the consolidation point (most recent) back to the newest\n\t\t\t\tfor (let i = mostRecentConsolidation; i >= 0; i--) {\n\t\t\t\t\tawait this.processNewSnapshots([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...syncStateSnapshots[i],\n\t\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\t\tisLocal: false\n\t\t\t\t\t\t}\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshotNoConsolidated\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tstorageKey\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\t// We have existing consolidated remote snapshots, so we can assume that we have\n\t\t\t// applied at least one consolidation snapshot, in this case we need to look at the changes since\n\t\t\t// then and apply them if we haven't already\n\t\t\t// We don't need to apply any additional consolidated snapshots, just the changesets\n\n\t\t\t// Create a lookup map for the existing snapshots\n\t\t\tconst existingSnapshotsMap: { [id: string]: SyncSnapshotEntry } = {};\n\t\t\tfor (const snapshot of existingSnapshots) {\n\t\t\t\texistingSnapshotsMap[snapshot.id] = snapshot;\n\t\t\t}\n\n\t\t\tconst newSnapshots: SyncSnapshotEntry[] = [];\n\t\t\tconst modifiedSnapshots: {\n\t\t\t\tcurrentSnapshot: SyncSnapshotEntry;\n\t\t\t\tupdatedSnapshot: SyncSnapshotEntry;\n\t\t\t}[] = [];\n\t\t\tconst referencedExistingSnapshots: string[] = Object.keys(existingSnapshotsMap);\n\n\t\t\tlet completedProcessing = false;\n\t\t\tfor (const snapshot of syncStateSnapshots) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\t\tmessage: \"applySnapshot\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tsnapshotId: snapshot.id,\n\t\t\t\t\t\tdateCreated: new Date(snapshot.dateCreated).toISOString()\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// See if we have the snapshot stored locally\n\t\t\t\tconst currentSnapshot = existingSnapshotsMap[snapshot.id];\n\n\t\t\t\t// As we are referencing an existing snapshot, we need to remove it from the list\n\t\t\t\t// to allow us to cleanup any unreferenced snapshots later\n\t\t\t\tconst idx = referencedExistingSnapshots.indexOf(snapshot.id);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\treferencedExistingSnapshots.splice(idx, 1);\n\t\t\t\t}\n\n\t\t\t\t// No need to apply consolidated snapshots\n\t\t\t\tif (!snapshot.isConsolidated && !completedProcessing) {\n\t\t\t\t\tconst updatedSnapshot: SyncSnapshotEntry = {\n\t\t\t\t\t\t...snapshot,\n\t\t\t\t\t\tstorageKey,\n\t\t\t\t\t\tisLocal: false\n\t\t\t\t\t};\n\n\t\t\t\t\tif (Is.empty(currentSnapshot)) {\n\t\t\t\t\t\t// We don't have the snapshot locally, so we need to process all of it\n\t\t\t\t\t\tnewSnapshots.push(updatedSnapshot);\n\t\t\t\t\t} else if (currentSnapshot.dateModified !== snapshot.dateModified) {\n\t\t\t\t\t\t// If the local snapshot has a different dateModified, we need to update it\n\t\t\t\t\t\tmodifiedSnapshots.push({\n\t\t\t\t\t\t\tcurrentSnapshot,\n\t\t\t\t\t\t\tupdatedSnapshot\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we sorted the snapshots from newest to oldest, so if we found a local snapshot\n\t\t\t\t\t\t// with the same dateModified as the remote snapshot, we can stop processing further\n\t\t\t\t\t\tcompletedProcessing = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// We reverse the order of the snapshots to process them from oldest to newest\n\t\t\t// because we want to apply the changes in the order they were created\n\t\t\tawait this.processModifiedSnapshots(modifiedSnapshots.reverse());\n\t\t\tawait this.processNewSnapshots(newSnapshots.reverse());\n\n\t\t\t// Any ids remaining in this list are no longer referenced in the global state\n\t\t\t// so we should remove them from the local storage as they will never be updated again\n\t\t\tfor (const referencedSnapshotId of referencedExistingSnapshots) {\n\t\t\t\tawait this._snapshotEntryEntityStorage.remove(referencedSnapshotId);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Process the modified snapshots and store them in the local storage.\n\t * @param modifiedSnapshots The modified snapshots to process.\n\t * @returns A promise that resolves when all new changesets in each snapshot have been applied and the snapshots saved.\n\t * @internal\n\t */\n\tprivate async processModifiedSnapshots(\n\t\tmodifiedSnapshots: {\n\t\t\tcurrentSnapshot: SyncSnapshotEntry;\n\t\t\tupdatedSnapshot: SyncSnapshotEntry;\n\t\t}[]\n\t): Promise<void> {\n\t\tfor (const modifiedSnapshot of modifiedSnapshots) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"processModifiedSnapshot\",\n\t\t\t\tdata: {\n\t\t\t\t\tsnapshotId: modifiedSnapshot.updatedSnapshot.id,\n\t\t\t\t\tlocalModified: new Date(\n\t\t\t\t\t\tmodifiedSnapshot.currentSnapshot.dateModified ??\n\t\t\t\t\t\t\tmodifiedSnapshot.currentSnapshot.dateCreated\n\t\t\t\t\t).toISOString(),\n\t\t\t\t\tremoteModified: new Date(\n\t\t\t\t\t\tmodifiedSnapshot.updatedSnapshot.dateModified ??\n\t\t\t\t\t\t\tmodifiedSnapshot.updatedSnapshot.dateCreated\n\t\t\t\t\t).toISOString()\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst remoteChangeSetStorageIds = modifiedSnapshot.updatedSnapshot.changeSetStorageIds;\n\t\t\tconst localChangeSetStorageIds = modifiedSnapshot.currentSnapshot.changeSetStorageIds ?? [];\n\t\t\tif (Is.arrayValue(remoteChangeSetStorageIds)) {\n\t\t\t\tfor (const storageId of remoteChangeSetStorageIds) {\n\t\t\t\t\t// Check if the local snapshot does not have the storageId\n\t\t\t\t\tif (!localChangeSetStorageIds.includes(storageId)) {\n\t\t\t\t\t\tawait this._changeSetHelper.getAndApplyChangeset(storageId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this._snapshotEntryEntityStorage.set(modifiedSnapshot.updatedSnapshot);\n\t\t}\n\t}\n\n\t/**\n\t * Process the new snapshots and store them in the local storage.\n\t * @param newSnapshots The new snapshots to process.\n\t * @returns A promise that resolves when all changesets for each snapshot have been applied and the snapshots saved.\n\t * @internal\n\t */\n\tprivate async processNewSnapshots(newSnapshots: SyncSnapshotEntry[]): Promise<void> {\n\t\tfor (const newSnapshot of newSnapshots) {\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: LocalSyncStateHelper.CLASS_NAME,\n\t\t\t\tmessage: \"processNewSnapshot\",\n\t\t\t\tdata: {\n\t\t\t\t\tsnapshotId: newSnapshot.id,\n\t\t\t\t\tdateCreated: newSnapshot.dateCreated\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst newSnapshotChangeSetStorageIds = newSnapshot.changeSetStorageIds ?? [];\n\t\t\tif (Is.arrayValue(newSnapshotChangeSetStorageIds)) {\n\t\t\t\tfor (const storageId of newSnapshotChangeSetStorageIds) {\n\t\t\t\t\tawait this._changeSetHelper.getAndApplyChangeset(storageId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this._snapshotEntryEntityStorage.set(newSnapshot);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -96,6 +96,7 @@ export class RemoteSyncStateHelper {
|
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
98
|
* Start the remote sync state helper.
|
|
99
|
+
* @returns A promise that resolves when all event bus subscriptions are registered.
|
|
99
100
|
*/
|
|
100
101
|
async start() {
|
|
101
102
|
await this._eventBusComponent.subscribe(SynchronisedStorageTopics.BatchResponse, async (response) => {
|
|
@@ -113,11 +114,11 @@ export class RemoteSyncStateHelper {
|
|
|
113
114
|
this._synchronisedStorageKey = synchronisedStorageKey;
|
|
114
115
|
}
|
|
115
116
|
/**
|
|
116
|
-
* Build a changeset.
|
|
117
|
+
* Build a changeset and invoke the callback when complete.
|
|
117
118
|
* @param storageKey The storage key of the change set.
|
|
118
119
|
* @param changes The changes to apply.
|
|
119
|
-
* @param completeCallback The callback
|
|
120
|
-
* @returns
|
|
120
|
+
* @param completeCallback The callback invoked when the changeset is created and stored.
|
|
121
|
+
* @returns A promise that resolves when item requests are dispatched or the changeset is finalised immediately.
|
|
121
122
|
*/
|
|
122
123
|
async buildChangeSet(storageKey, changes, completeCallback) {
|
|
123
124
|
await this._logging?.log({
|
|
@@ -165,8 +166,8 @@ export class RemoteSyncStateHelper {
|
|
|
165
166
|
/**
|
|
166
167
|
* Finalise the full details for the sync change set.
|
|
167
168
|
* @param storageKey The storage key of the change set.
|
|
168
|
-
* @param completeCallback The callback
|
|
169
|
-
* @returns
|
|
169
|
+
* @param completeCallback The callback invoked when the changeset is populated and optionally stored.
|
|
170
|
+
* @returns A promise that resolves when the changeset is built and the callback is invoked.
|
|
170
171
|
*/
|
|
171
172
|
async finaliseFullChanges(storageKey, completeCallback) {
|
|
172
173
|
await this._logging?.log({
|
|
@@ -229,8 +230,8 @@ export class RemoteSyncStateHelper {
|
|
|
229
230
|
/**
|
|
230
231
|
* Add a new changeset into the sync state.
|
|
231
232
|
* @param storageKey The storage key of the change set to add.
|
|
232
|
-
* @param changeSetStorageId The id of the change set to add
|
|
233
|
-
* @returns
|
|
233
|
+
* @param changeSetStorageId The id of the change set to add.
|
|
234
|
+
* @returns A promise that resolves when the sync state and verifiable sync pointer are updated.
|
|
234
235
|
*/
|
|
235
236
|
async addChangeSetToSyncState(storageKey, changeSetStorageId) {
|
|
236
237
|
await this._logging?.log({
|
|
@@ -287,7 +288,7 @@ export class RemoteSyncStateHelper {
|
|
|
287
288
|
* Create a consolidated snapshot for the entire storage.
|
|
288
289
|
* @param storageKey The storage key of the snapshot to create.
|
|
289
290
|
* @param batchSize The batch size to use for consolidation.
|
|
290
|
-
* @returns
|
|
291
|
+
* @returns A promise that resolves when the batch request is published to the event bus.
|
|
291
292
|
*/
|
|
292
293
|
async consolidationStart(storageKey, batchSize) {
|
|
293
294
|
await this._logging?.log({
|
|
@@ -350,7 +351,7 @@ export class RemoteSyncStateHelper {
|
|
|
350
351
|
/**
|
|
351
352
|
* Store the verifiable sync pointer in the verifiable storage.
|
|
352
353
|
* @param syncPointerStore The sync pointer store to store.
|
|
353
|
-
* @returns
|
|
354
|
+
* @returns A promise that resolves when the pointer store is persisted to verifiable storage.
|
|
354
355
|
*/
|
|
355
356
|
async storeVerifiableSyncPointerStore(syncPointerStore) {
|
|
356
357
|
if (Is.stringValue(this._nodeId) && Is.stringValue(this._synchronisedStorageKey)) {
|
|
@@ -460,6 +461,8 @@ export class RemoteSyncStateHelper {
|
|
|
460
461
|
/**
|
|
461
462
|
* Handle the batch response which is triggered from a consolidation request.
|
|
462
463
|
* @param response The batch response to handle.
|
|
464
|
+
* @returns A promise that resolves when the batch is processed and, if it is the last entry, the consolidated snapshot is stored.
|
|
465
|
+
* @internal
|
|
463
466
|
*/
|
|
464
467
|
async handleBatchResponse(response) {
|
|
465
468
|
if (Is.stringValue(this._nodeId)) {
|
|
@@ -531,6 +534,8 @@ export class RemoteSyncStateHelper {
|
|
|
531
534
|
/**
|
|
532
535
|
* Handle the item response.
|
|
533
536
|
* @param response The item response to handle.
|
|
537
|
+
* @returns A promise that resolves when the entity is recorded and the complete callback is invoked if all requests are fulfilled.
|
|
538
|
+
* @internal
|
|
534
539
|
*/
|
|
535
540
|
async handleLocalItemResponse(response) {
|
|
536
541
|
await this._logging?.log({
|