@carlonicora/nestjs-neo4jsonapi 8.15.0 → 8.16.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/dist/bootstrap/app.module.factory.d.ts.map +1 -1
- package/dist/bootstrap/app.module.factory.js +9 -0
- package/dist/bootstrap/app.module.factory.js.map +1 -1
- package/dist/bootstrap/bootstrap.options.d.ts +10 -0
- package/dist/bootstrap/bootstrap.options.d.ts.map +1 -1
- package/dist/common/helpers/define-entity.d.ts.map +1 -1
- package/dist/common/helpers/define-entity.js +3 -0
- package/dist/common/helpers/define-entity.js.map +1 -1
- package/dist/common/registries/entity-descriptor.registry.d.ts +22 -0
- package/dist/common/registries/entity-descriptor.registry.d.ts.map +1 -0
- package/dist/common/registries/entity-descriptor.registry.js +51 -0
- package/dist/common/registries/entity-descriptor.registry.js.map +1 -0
- package/dist/common/registries/index.d.ts +1 -0
- package/dist/common/registries/index.d.ts.map +1 -1
- package/dist/common/registries/index.js +1 -0
- package/dist/common/registries/index.js.map +1 -1
- package/dist/core/cors/services/cors.service.d.ts +4 -1
- package/dist/core/cors/services/cors.service.d.ts.map +1 -1
- package/dist/core/cors/services/cors.service.js +19 -3
- package/dist/core/cors/services/cors.service.js.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.repository.d.ts +2 -0
- package/dist/core/neo4j/abstracts/abstract.repository.d.ts.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.repository.js +4 -1
- package/dist/core/neo4j/abstracts/abstract.repository.js.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.service.d.ts +6 -0
- package/dist/core/neo4j/abstracts/abstract.service.d.ts.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.service.js +16 -0
- package/dist/core/neo4j/abstracts/abstract.service.js.map +1 -1
- package/dist/core/neo4j/helpers/build-filter-clauses.d.ts.map +1 -1
- package/dist/core/neo4j/helpers/build-filter-clauses.js +8 -1
- package/dist/core/neo4j/helpers/build-filter-clauses.js.map +1 -1
- package/dist/core/offline/index.d.ts +5 -0
- package/dist/core/offline/index.d.ts.map +1 -0
- package/dist/core/offline/index.js +21 -0
- package/dist/core/offline/index.js.map +1 -0
- package/dist/core/offline/offline-request.context.d.ts +12 -0
- package/dist/core/offline/offline-request.context.d.ts.map +1 -0
- package/dist/core/offline/offline-request.context.js +34 -0
- package/dist/core/offline/offline-request.context.js.map +1 -0
- package/dist/core/offline/offline-request.interceptor.d.ts +15 -0
- package/dist/core/offline/offline-request.interceptor.d.ts.map +1 -0
- package/dist/core/offline/offline-request.interceptor.js +43 -0
- package/dist/core/offline/offline-request.interceptor.js.map +1 -0
- package/dist/core/offline/offline.constants.d.ts +12 -0
- package/dist/core/offline/offline.constants.d.ts.map +1 -0
- package/dist/core/offline/offline.constants.js +12 -0
- package/dist/core/offline/offline.constants.js.map +1 -0
- package/dist/core/offline/offline.module.d.ts +12 -0
- package/dist/core/offline/offline.module.d.ts.map +1 -0
- package/dist/core/offline/offline.module.js +42 -0
- package/dist/core/offline/offline.module.js.map +1 -0
- package/dist/foundations/chunker/services/types/pdf.service.d.ts.map +1 -1
- package/dist/foundations/chunker/services/types/pdf.service.js +9 -5
- package/dist/foundations/chunker/services/types/pdf.service.js.map +1 -1
- package/dist/foundations/offline-change-set/decorators/offline-replayable.decorator.d.ts +7 -0
- package/dist/foundations/offline-change-set/decorators/offline-replayable.decorator.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/decorators/offline-replayable.decorator.js +12 -0
- package/dist/foundations/offline-change-set/decorators/offline-replayable.decorator.js.map +1 -0
- package/dist/foundations/offline-change-set/dtos/offline-change-set.post.dto.d.ts +33 -0
- package/dist/foundations/offline-change-set/dtos/offline-change-set.post.dto.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/dtos/offline-change-set.post.dto.js +133 -0
- package/dist/foundations/offline-change-set/dtos/offline-change-set.post.dto.js.map +1 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.d.ts +66 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.js +31 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.js.map +1 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.meta.d.ts +3 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.meta.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.meta.js +10 -0
- package/dist/foundations/offline-change-set/entities/offline-change-set.meta.js.map +1 -0
- package/dist/foundations/offline-change-set/index.d.ts +11 -0
- package/dist/foundations/offline-change-set/index.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/index.js +27 -0
- package/dist/foundations/offline-change-set/index.js.map +1 -0
- package/dist/foundations/offline-change-set/merge/deep-equal.d.ts +6 -0
- package/dist/foundations/offline-change-set/merge/deep-equal.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/merge/deep-equal.js +31 -0
- package/dist/foundations/offline-change-set/merge/deep-equal.js.map +1 -0
- package/dist/foundations/offline-change-set/merge/merge-blocks.d.ts +37 -0
- package/dist/foundations/offline-change-set/merge/merge-blocks.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/merge/merge-blocks.js +108 -0
- package/dist/foundations/offline-change-set/merge/merge-blocks.js.map +1 -0
- package/dist/foundations/offline-change-set/merge/three-way-merge.d.ts +57 -0
- package/dist/foundations/offline-change-set/merge/three-way-merge.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/merge/three-way-merge.js +201 -0
- package/dist/foundations/offline-change-set/merge/three-way-merge.js.map +1 -0
- package/dist/foundations/offline-change-set/offline-change-set.module.d.ts +11 -0
- package/dist/foundations/offline-change-set/offline-change-set.module.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/offline-change-set.module.js +41 -0
- package/dist/foundations/offline-change-set/offline-change-set.module.js.map +1 -0
- package/dist/foundations/offline-change-set/repositories/offline-change-set.repository.d.ts +26 -0
- package/dist/foundations/offline-change-set/repositories/offline-change-set.repository.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/repositories/offline-change-set.repository.js +56 -0
- package/dist/foundations/offline-change-set/repositories/offline-change-set.repository.js.map +1 -0
- package/dist/foundations/offline-change-set/services/offline-change-set.service.d.ts +72 -0
- package/dist/foundations/offline-change-set/services/offline-change-set.service.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/services/offline-change-set.service.js +333 -0
- package/dist/foundations/offline-change-set/services/offline-change-set.service.js.map +1 -0
- package/dist/foundations/offline-change-set/services/offline-route-runner.service.d.ts +61 -0
- package/dist/foundations/offline-change-set/services/offline-route-runner.service.d.ts.map +1 -0
- package/dist/foundations/offline-change-set/services/offline-route-runner.service.js +192 -0
- package/dist/foundations/offline-change-set/services/offline-route-runner.service.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.OfflineChangeSetModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const core_1 = require("@nestjs/core");
|
|
12
|
+
const registry_1 = require("../../common/registries/registry");
|
|
13
|
+
const offline_change_set_1 = require("./entities/offline-change-set");
|
|
14
|
+
const offline_change_set_repository_1 = require("./repositories/offline-change-set.repository");
|
|
15
|
+
const offline_change_set_service_1 = require("./services/offline-change-set.service");
|
|
16
|
+
const offline_route_runner_service_1 = require("./services/offline-route-runner.service");
|
|
17
|
+
/**
|
|
18
|
+
* Offline change sets: opens a package of every change a device queued while offline and runs
|
|
19
|
+
* each one through the app's own routes. Loaded by `OfflineModule` when
|
|
20
|
+
* `bootstrap({ offline: { changeSets: true } })`. Declares no controller: the receiving route
|
|
21
|
+
* (`POST /offline-change-sets`) belongs to the app, which calls `OfflineChangeSetService.apply`.
|
|
22
|
+
*/
|
|
23
|
+
let OfflineChangeSetModule = class OfflineChangeSetModule {
|
|
24
|
+
onModuleInit() {
|
|
25
|
+
registry_1.modelRegistry.register(offline_change_set_1.OfflineChangeSetDescriptor.model);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.OfflineChangeSetModule = OfflineChangeSetModule;
|
|
29
|
+
exports.OfflineChangeSetModule = OfflineChangeSetModule = __decorate([
|
|
30
|
+
(0, common_1.Module)({
|
|
31
|
+
imports: [core_1.DiscoveryModule],
|
|
32
|
+
providers: [
|
|
33
|
+
offline_change_set_1.OfflineChangeSetDescriptor.model.serialiser,
|
|
34
|
+
offline_change_set_repository_1.OfflineChangeSetRepository,
|
|
35
|
+
offline_change_set_service_1.OfflineChangeSetService,
|
|
36
|
+
offline_route_runner_service_1.OfflineRouteRunner,
|
|
37
|
+
],
|
|
38
|
+
exports: [offline_change_set_service_1.OfflineChangeSetService],
|
|
39
|
+
})
|
|
40
|
+
], OfflineChangeSetModule);
|
|
41
|
+
//# sourceMappingURL=offline-change-set.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-change-set.module.js","sourceRoot":"","sources":["../../../src/foundations/offline-change-set/offline-change-set.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAsD;AACtD,uCAA+C;AAC/C,+DAAiE;AACjE,sEAA2E;AAC3E,gGAA0F;AAC1F,sFAAgF;AAChF,0FAA6E;AAE7E;;;;;GAKG;AAWI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,YAAY;QACV,wBAAa,CAAC,QAAQ,CAAC,+CAA0B,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;CACF,CAAA;AAJY,wDAAsB;iCAAtB,sBAAsB;IAVlC,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,sBAAe,CAAC;QAC1B,SAAS,EAAE;YACT,+CAA0B,CAAC,KAAK,CAAC,UAAU;YAC3C,0DAA0B;YAC1B,oDAAuB;YACvB,iDAAkB;SACnB;QACD,OAAO,EAAE,CAAC,oDAAuB,CAAC;KACnC,CAAC;GACW,sBAAsB,CAIlC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ClsService } from "nestjs-cls";
|
|
2
|
+
import { AbstractRepository } from "../../../core/neo4j/abstracts/abstract.repository";
|
|
3
|
+
import { Neo4jService } from "../../../core/neo4j/services/neo4j.service";
|
|
4
|
+
import { SecurityService } from "../../../core/security/services/security.service";
|
|
5
|
+
import { OfflineChangeSet, OfflineChangeSetDescriptor } from "../entities/offline-change-set";
|
|
6
|
+
/**
|
|
7
|
+
* Repository of the stored offline change sets (one `OfflineChangeSet` node of the caller's
|
|
8
|
+
* company per package opened, holding its results). The service uses the inherited,
|
|
9
|
+
* company-scoped `findById` (a package sent again) and `create` (a package just opened), and
|
|
10
|
+
* `deleteOlderThan` (clean-up of old ones); every read and write of a change itself runs
|
|
11
|
+
* through the app's own routes (`OfflineRouteRunner`).
|
|
12
|
+
* The inherited `onModuleInit` creates the id constraint.
|
|
13
|
+
*/
|
|
14
|
+
export declare class OfflineChangeSetRepository extends AbstractRepository<OfflineChangeSet, typeof OfflineChangeSetDescriptor.relationships> {
|
|
15
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<OfflineChangeSet, {}>;
|
|
16
|
+
constructor(neo4j: Neo4jService, securityService: SecurityService, clsService: ClsService);
|
|
17
|
+
/**
|
|
18
|
+
* Deletes the request company's stored change sets created before `before`. Without a company
|
|
19
|
+
* in the request it deletes nothing: `buildDefaultMatch()` matches every company's nodes when
|
|
20
|
+
* `$companyId` is null.
|
|
21
|
+
*/
|
|
22
|
+
deleteOlderThan(params: {
|
|
23
|
+
before: Date;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=offline-change-set.repository.d.ts.map
|
package/dist/foundations/offline-change-set/repositories/offline-change-set.repository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-change-set.repository.d.ts","sourceRoot":"","sources":["../../../../src/foundations/offline-change-set/repositories/offline-change-set.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAE9F;;;;;;;GAOG;AACH,qBACa,0BAA2B,SAAQ,kBAAkB,CAChE,gBAAgB,EAChB,OAAO,0BAA0B,CAAC,aAAa,CAChD;IACC,SAAS,CAAC,QAAQ,CAAC,UAAU,4DAA8B;gBAE/C,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;IAIzF;;;;OAIG;IACG,eAAe,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAe/D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.OfflineChangeSetRepository = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
15
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
16
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
17
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
18
|
+
const offline_change_set_1 = require("../entities/offline-change-set");
|
|
19
|
+
/**
|
|
20
|
+
* Repository of the stored offline change sets (one `OfflineChangeSet` node of the caller's
|
|
21
|
+
* company per package opened, holding its results). The service uses the inherited,
|
|
22
|
+
* company-scoped `findById` (a package sent again) and `create` (a package just opened), and
|
|
23
|
+
* `deleteOlderThan` (clean-up of old ones); every read and write of a change itself runs
|
|
24
|
+
* through the app's own routes (`OfflineRouteRunner`).
|
|
25
|
+
* The inherited `onModuleInit` creates the id constraint.
|
|
26
|
+
*/
|
|
27
|
+
let OfflineChangeSetRepository = class OfflineChangeSetRepository extends abstract_repository_1.AbstractRepository {
|
|
28
|
+
constructor(neo4j, securityService, clsService) {
|
|
29
|
+
super(neo4j, securityService, clsService);
|
|
30
|
+
this.descriptor = offline_change_set_1.OfflineChangeSetDescriptor;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Deletes the request company's stored change sets created before `before`. Without a company
|
|
34
|
+
* in the request it deletes nothing: `buildDefaultMatch()` matches every company's nodes when
|
|
35
|
+
* `$companyId` is null.
|
|
36
|
+
*/
|
|
37
|
+
async deleteOlderThan(params) {
|
|
38
|
+
const { nodeName } = this.descriptor.model;
|
|
39
|
+
const query = this.neo4j.initQuery({ serialiser: this.descriptor.model });
|
|
40
|
+
if (!query.queryParams.companyId)
|
|
41
|
+
return;
|
|
42
|
+
query.queryParams = { ...query.queryParams, before: params.before.toISOString() };
|
|
43
|
+
query.query += `
|
|
44
|
+
${this.buildDefaultMatch()}
|
|
45
|
+
WHERE ${nodeName}.createdAt < datetime($before)
|
|
46
|
+
DETACH DELETE ${nodeName}
|
|
47
|
+
`;
|
|
48
|
+
await this.neo4j.writeOne(query);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
exports.OfflineChangeSetRepository = OfflineChangeSetRepository;
|
|
52
|
+
exports.OfflineChangeSetRepository = OfflineChangeSetRepository = __decorate([
|
|
53
|
+
(0, common_1.Injectable)(),
|
|
54
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
55
|
+
], OfflineChangeSetRepository);
|
|
56
|
+
//# sourceMappingURL=offline-change-set.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-change-set.repository.js","sourceRoot":"","sources":["../../../../src/foundations/offline-change-set/repositories/offline-change-set.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAAwC;AACxC,2FAAuF;AACvF,8EAA0E;AAC1E,uFAAmF;AACnF,uEAA8F;AAE9F;;;;;;;GAOG;AAEI,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,wCAG/C;IAGC,YAAY,KAAmB,EAAE,eAAgC,EAAE,UAAsB;QACvF,KAAK,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAHzB,eAAU,GAAG,+CAA0B,CAAC;IAI3D,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe,CAAC,MAAwB;QAC5C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS;YAAE,OAAO;QAEzC,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAElF,KAAK,CAAC,KAAK,IAAI;QACX,IAAI,CAAC,iBAAiB,EAAE;cAClB,QAAQ;sBACA,QAAQ;KACzB,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;CACF,CAAA;AA9BY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,mBAAU,GAAE;qCAOQ,4BAAY,EAAmB,kCAAe,EAAc,uBAAU;GAN9E,0BAA0B,CA8BtC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { FastifyRequest } from "fastify";
|
|
2
|
+
import { ClsService } from "nestjs-cls";
|
|
3
|
+
import { JsonApiSingleDocument } from "../../../core/jsonapi/interfaces/jsonapi.document.interface";
|
|
4
|
+
import { JsonApiService } from "../../../core/jsonapi/services/jsonapi.service";
|
|
5
|
+
import { AbstractService, JsonApiDTOData } from "../../../core/neo4j/abstracts/abstract.service";
|
|
6
|
+
import { OfflineChangeSet, OfflineChangeSetDescriptor } from "../entities/offline-change-set";
|
|
7
|
+
import { OfflineChangeSetRepository } from "../repositories/offline-change-set.repository";
|
|
8
|
+
import { OfflineRouteRunner } from "./offline-route-runner.service";
|
|
9
|
+
/**
|
|
10
|
+
* Opens one offline change set (every change the device queued, in one request) and handles
|
|
11
|
+
* each change in `seq` order through the app's own routes, as the caller (`OfflineRouteRunner`).
|
|
12
|
+
*
|
|
13
|
+
* - POST, and any write that names no record, runs as sent.
|
|
14
|
+
* - PUT/PATCH of a record: the current record is read (GET on the record's own path,
|
|
15
|
+
* `<endpoint>/<id>`) and merged with `base` and the device's document (`threeWayMerge`);
|
|
16
|
+
* only the changed fields are written, as a PATCH of the device's document trimmed to them
|
|
17
|
+
* (merged rich text in place). A change carrying `resolution` merges the same way, and each
|
|
18
|
+
* conflicting field takes the GM's choice.
|
|
19
|
+
* - DELETE of a record: refused as "changed on the server" when a descriptor field or a
|
|
20
|
+
* linked id changed since `base`; with `resolution` (delete anyway) it runs without that check.
|
|
21
|
+
* - After a conflict or a could-not-apply, later changes to the same record wait (a custom
|
|
22
|
+
* write `<endpoint>/<id>/...` counts as a change of that record).
|
|
23
|
+
* - A package is opened once: its results are stored under its id (company-scoped) and a
|
|
24
|
+
* package sent again with the same id is answered with them, without running anything.
|
|
25
|
+
*
|
|
26
|
+
* The package is never paged, batched or capped.
|
|
27
|
+
*/
|
|
28
|
+
export declare class OfflineChangeSetService extends AbstractService<OfflineChangeSet, typeof OfflineChangeSetDescriptor.relationships> {
|
|
29
|
+
private readonly offlineChangeSetRepository;
|
|
30
|
+
private readonly runner;
|
|
31
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<OfflineChangeSet, {}>;
|
|
32
|
+
private readonly logger;
|
|
33
|
+
constructor(jsonApiService: JsonApiService, offlineChangeSetRepository: OfflineChangeSetRepository, clsService: ClsService, runner: OfflineRouteRunner);
|
|
34
|
+
apply(params: {
|
|
35
|
+
data: JsonApiDTOData;
|
|
36
|
+
request: FastifyRequest;
|
|
37
|
+
}): Promise<JsonApiSingleDocument>;
|
|
38
|
+
/**
|
|
39
|
+
* Removes the company's stored change sets older than `STORED_CHANGE_SET_DAYS`: a device sends a
|
|
40
|
+
* package again only right after a failed answer, never days later. A failed clean-up is
|
|
41
|
+
* logged and never stops the package.
|
|
42
|
+
*/
|
|
43
|
+
private removeExpired;
|
|
44
|
+
/** The JSON:API type answering on an endpoint (the type is the endpoint unless a descriptor says otherwise). */
|
|
45
|
+
private typeOfEndpoint;
|
|
46
|
+
/** The record's own path: `<its type's endpoint>/<id>`. */
|
|
47
|
+
private recordPath;
|
|
48
|
+
/** The record a change touches: its record fields, the resource a create posts, or the `<endpoint>/<id>` its path targets. */
|
|
49
|
+
private recordKey;
|
|
50
|
+
private applyChange;
|
|
51
|
+
private runAsSent;
|
|
52
|
+
private toResult;
|
|
53
|
+
private readCurrent;
|
|
54
|
+
/**
|
|
55
|
+
* Relationship names the record's GET always answers when it has a link (the serialiser
|
|
56
|
+
* emits every descriptor relationship and omits only an empty one), so an omitted one is empty.
|
|
57
|
+
*/
|
|
58
|
+
private knownRelationships;
|
|
59
|
+
private mergeResolution;
|
|
60
|
+
private applyEdit;
|
|
61
|
+
private applyDelete;
|
|
62
|
+
/**
|
|
63
|
+
* The attributes a change could have touched: the descriptor's fields that go on the wire as
|
|
64
|
+
* stored (no transform, such as a signed URL; not meta; not excluded), minus createdAt/updatedAt
|
|
65
|
+
* and the fields only the server writes (`SERVER_WRITTEN_ATTRIBUTES`, `immutable` fields).
|
|
66
|
+
* `undefined` for an unknown type (every attribute base holds is compared, minus those names).
|
|
67
|
+
*/
|
|
68
|
+
private comparableAttributes;
|
|
69
|
+
/** A descriptor field or a linked id of the record differs from `base`. */
|
|
70
|
+
private changedSince;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=offline-change-set.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-change-set.service.d.ts","sourceRoot":"","sources":["../../../../src/foundations/offline-change-set/services/offline-change-set.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,qBAAqB,EAAE,MAAM,6DAA6D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,gDAAgD,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gDAAgD,CAAC;AACjG,OAAO,EAGL,gBAAgB,EAChB,0BAA0B,EAC3B,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAC3F,OAAO,EAAsB,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AA+CxF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,uBAAwB,SAAQ,eAAe,CAC1D,gBAAgB,EAChB,OAAO,0BAA0B,CAAC,aAAa,CAChD;IAMG,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,SAAS,CAAC,QAAQ,CAAC,UAAU,4DAA8B;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAGjE,cAAc,EAAE,cAAc,EACb,0BAA0B,EAAE,0BAA0B,EACvE,UAAU,EAAE,UAAU,EACL,MAAM,EAAE,kBAAkB;IAKvC,KAAK,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA2CtG;;;;OAIG;YACW,aAAa;IAS3B,gHAAgH;IAChH,OAAO,CAAC,cAAc;IAItB,2DAA2D;IAC3D,OAAO,CAAC,UAAU;IAMlB,8HAA8H;IAC9H,OAAO,CAAC,SAAS;YAUH,WAAW;YAOX,SAAS;IAKvB,OAAO,CAAC,QAAQ;YASF,WAAW;IAUzB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,eAAe;YAWT,SAAS;YAqDT,WAAW;IAgBzB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAkB5B,2EAA2E;IAC3E,OAAO,CAAC,YAAY;CAgBrB"}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var OfflineChangeSetService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.OfflineChangeSetService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
16
|
+
const entity_descriptor_registry_1 = require("../../../common/registries/entity-descriptor.registry");
|
|
17
|
+
const jsonapi_service_1 = require("../../../core/jsonapi/services/jsonapi.service");
|
|
18
|
+
const abstract_service_1 = require("../../../core/neo4j/abstracts/abstract.service");
|
|
19
|
+
const offline_change_set_1 = require("../entities/offline-change-set");
|
|
20
|
+
const deep_equal_1 = require("../merge/deep-equal");
|
|
21
|
+
const three_way_merge_1 = require("../merge/three-way-merge");
|
|
22
|
+
const offline_change_set_repository_1 = require("../repositories/offline-change-set.repository");
|
|
23
|
+
const offline_route_runner_service_1 = require("./offline-route-runner.service");
|
|
24
|
+
const isOk = (status) => status >= 200 && status < 300;
|
|
25
|
+
/** The reason an app route gave for refusing a step (JSON:API errors, Nest's error body, or the status). */
|
|
26
|
+
function errorDetail(status, body) {
|
|
27
|
+
const error = Array.isArray(body?.errors) ? body.errors[0] : undefined;
|
|
28
|
+
if (error?.detail)
|
|
29
|
+
return String(error.detail);
|
|
30
|
+
if (error?.title)
|
|
31
|
+
return String(error.title);
|
|
32
|
+
if (Array.isArray(body?.message))
|
|
33
|
+
return body.message.join("; ");
|
|
34
|
+
if (typeof body?.message === "string" && body.message)
|
|
35
|
+
return body.message;
|
|
36
|
+
if (typeof body === "string" && body)
|
|
37
|
+
return body;
|
|
38
|
+
return `HTTP ${status}`;
|
|
39
|
+
}
|
|
40
|
+
/** Values the server sets on every write or recomputes on every read: never a change by anyone. */
|
|
41
|
+
const VOLATILE_ATTRIBUTES = new Set(["createdAt", "updatedAt"]);
|
|
42
|
+
/**
|
|
43
|
+
* Attributes only the server (the AI workers) writes. Descriptors carry no marker for them (the
|
|
44
|
+
* app declares them as plain fields), so they are named here; a field marked `immutable` (never
|
|
45
|
+
* written by a client save) counts too. A change to them is never "changed on the server".
|
|
46
|
+
*/
|
|
47
|
+
const SERVER_WRITTEN_ATTRIBUTES = new Set(["aiStatus", "summary", "tldr"]);
|
|
48
|
+
/** How long a stored change set (a package's results, kept for a resend) is kept. */
|
|
49
|
+
const STORED_CHANGE_SET_DAYS = 7;
|
|
50
|
+
/** `npcs/n1?x`, `/npcs/n1` or an absolute URL → `["npcs", "n1"]`. */
|
|
51
|
+
function pathSegments(path) {
|
|
52
|
+
let pathname = path.split("?")[0];
|
|
53
|
+
if (/^https?:\/\//i.test(pathname))
|
|
54
|
+
pathname = new URL(pathname).pathname;
|
|
55
|
+
return pathname.split("/").filter((s) => s.length > 0);
|
|
56
|
+
}
|
|
57
|
+
const linkageIds = (rel) => {
|
|
58
|
+
if (!rel || typeof rel !== "object" || !("data" in rel))
|
|
59
|
+
return undefined;
|
|
60
|
+
const data = rel.data;
|
|
61
|
+
if (data == null)
|
|
62
|
+
return [];
|
|
63
|
+
return (Array.isArray(data) ? data : [data]).map((d) => d.id).sort();
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Opens one offline change set (every change the device queued, in one request) and handles
|
|
67
|
+
* each change in `seq` order through the app's own routes, as the caller (`OfflineRouteRunner`).
|
|
68
|
+
*
|
|
69
|
+
* - POST, and any write that names no record, runs as sent.
|
|
70
|
+
* - PUT/PATCH of a record: the current record is read (GET on the record's own path,
|
|
71
|
+
* `<endpoint>/<id>`) and merged with `base` and the device's document (`threeWayMerge`);
|
|
72
|
+
* only the changed fields are written, as a PATCH of the device's document trimmed to them
|
|
73
|
+
* (merged rich text in place). A change carrying `resolution` merges the same way, and each
|
|
74
|
+
* conflicting field takes the GM's choice.
|
|
75
|
+
* - DELETE of a record: refused as "changed on the server" when a descriptor field or a
|
|
76
|
+
* linked id changed since `base`; with `resolution` (delete anyway) it runs without that check.
|
|
77
|
+
* - After a conflict or a could-not-apply, later changes to the same record wait (a custom
|
|
78
|
+
* write `<endpoint>/<id>/...` counts as a change of that record).
|
|
79
|
+
* - A package is opened once: its results are stored under its id (company-scoped) and a
|
|
80
|
+
* package sent again with the same id is answered with them, without running anything.
|
|
81
|
+
*
|
|
82
|
+
* The package is never paged, batched or capped.
|
|
83
|
+
*/
|
|
84
|
+
let OfflineChangeSetService = OfflineChangeSetService_1 = class OfflineChangeSetService extends abstract_service_1.AbstractService {
|
|
85
|
+
constructor(jsonApiService, offlineChangeSetRepository, clsService, runner) {
|
|
86
|
+
super(jsonApiService, offlineChangeSetRepository, clsService, offline_change_set_1.OfflineChangeSetDescriptor.model);
|
|
87
|
+
this.offlineChangeSetRepository = offlineChangeSetRepository;
|
|
88
|
+
this.runner = runner;
|
|
89
|
+
this.descriptor = offline_change_set_1.OfflineChangeSetDescriptor;
|
|
90
|
+
this.logger = new common_1.Logger(OfflineChangeSetService_1.name);
|
|
91
|
+
}
|
|
92
|
+
async apply(params) {
|
|
93
|
+
const id = params.data.id;
|
|
94
|
+
await this.removeExpired();
|
|
95
|
+
const stored = await this.repository.findById({ id });
|
|
96
|
+
if (stored)
|
|
97
|
+
return this.jsonApiService.buildSingle(offline_change_set_1.OfflineChangeSetDescriptor.model, stored);
|
|
98
|
+
const changes = [...(params.data.attributes?.changes ?? [])].sort((a, b) => a.seq - b.seq);
|
|
99
|
+
const blocked = new Set();
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const change of changes) {
|
|
102
|
+
const key = this.recordKey(change);
|
|
103
|
+
if (key && blocked.has(key)) {
|
|
104
|
+
results.push({ seq: change.seq, status: "waiting" });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let result;
|
|
108
|
+
try {
|
|
109
|
+
result = await this.applyChange(change, params.request);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
result = { seq: change.seq, status: "could-not-apply", detail: error?.message ?? String(error) };
|
|
113
|
+
}
|
|
114
|
+
if (key && (result.status === "conflict" || result.status === "could-not-apply"))
|
|
115
|
+
blocked.add(key);
|
|
116
|
+
results.push(result);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
await this.repository.create({ id, resultsJson: JSON.stringify(results) });
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// The changes ran: answer them. A package sent again would run once more, as before this store existed.
|
|
123
|
+
this.logger.error(`offline change set ${id}: results not stored: ${error?.message ?? error}`);
|
|
124
|
+
}
|
|
125
|
+
return this.jsonApiService.buildSingle(offline_change_set_1.OfflineChangeSetDescriptor.model, { id, results });
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Removes the company's stored change sets older than `STORED_CHANGE_SET_DAYS`: a device sends a
|
|
129
|
+
* package again only right after a failed answer, never days later. A failed clean-up is
|
|
130
|
+
* logged and never stops the package.
|
|
131
|
+
*/
|
|
132
|
+
async removeExpired() {
|
|
133
|
+
const before = new Date(Date.now() - STORED_CHANGE_SET_DAYS * 24 * 60 * 60 * 1000);
|
|
134
|
+
try {
|
|
135
|
+
await this.offlineChangeSetRepository.deleteOlderThan({ before });
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
this.logger.error(`offline change sets: clean-up failed: ${error?.message ?? error}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** The JSON:API type answering on an endpoint (the type is the endpoint unless a descriptor says otherwise). */
|
|
142
|
+
typeOfEndpoint(endpoint) {
|
|
143
|
+
return entity_descriptor_registry_1.entityDescriptorRegistry.getByEndpoint(endpoint)?.model?.type ?? endpoint;
|
|
144
|
+
}
|
|
145
|
+
/** The record's own path: `<its type's endpoint>/<id>`. */
|
|
146
|
+
recordPath(change) {
|
|
147
|
+
const type = change.recordType;
|
|
148
|
+
const endpoint = entity_descriptor_registry_1.entityDescriptorRegistry.get(type)?.model?.endpoint ?? type;
|
|
149
|
+
return `${endpoint}/${change.recordId}`;
|
|
150
|
+
}
|
|
151
|
+
/** The record a change touches: its record fields, the resource a create posts, or the `<endpoint>/<id>` its path targets. */
|
|
152
|
+
recordKey(change) {
|
|
153
|
+
if (change.recordType && change.recordId)
|
|
154
|
+
return `${change.recordType}:${change.recordId}`;
|
|
155
|
+
const data = change.body?.data;
|
|
156
|
+
if (change.method === "POST" && typeof data?.type === "string" && typeof data?.id === "string")
|
|
157
|
+
return `${data.type}:${data.id}`;
|
|
158
|
+
const [endpoint, recordId] = pathSegments(change.path);
|
|
159
|
+
if (endpoint && recordId)
|
|
160
|
+
return `${this.typeOfEndpoint(endpoint)}:${recordId}`;
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
async applyChange(change, request) {
|
|
164
|
+
const namesRecord = !!change.recordType && !!change.recordId;
|
|
165
|
+
if (!namesRecord || change.method === "POST")
|
|
166
|
+
return this.runAsSent(change, request);
|
|
167
|
+
if (change.method === "DELETE")
|
|
168
|
+
return this.applyDelete(change, request);
|
|
169
|
+
return this.applyEdit(change, request);
|
|
170
|
+
}
|
|
171
|
+
async runAsSent(change, request) {
|
|
172
|
+
const answer = await this.runner.run({ method: change.method, path: change.path, body: change.body, request });
|
|
173
|
+
return this.toResult(change.seq, answer);
|
|
174
|
+
}
|
|
175
|
+
toResult(seq, answer) {
|
|
176
|
+
if (answer.ran === false)
|
|
177
|
+
return { seq, status: "could-not-apply", detail: answer.detail };
|
|
178
|
+
if (!isOk(answer.status))
|
|
179
|
+
return { seq, status: "could-not-apply", detail: errorDetail(answer.status, answer.body) };
|
|
180
|
+
return answer.body && typeof answer.body === "object"
|
|
181
|
+
? { seq, status: "applied", response: answer.body }
|
|
182
|
+
: { seq, status: "applied" };
|
|
183
|
+
}
|
|
184
|
+
async readCurrent(change, request) {
|
|
185
|
+
const answer = await this.runner.run({ method: "GET", path: this.recordPath(change), request });
|
|
186
|
+
if (answer.ran === false)
|
|
187
|
+
return { kind: "failed", detail: answer.detail };
|
|
188
|
+
if (answer.status === 404)
|
|
189
|
+
return { kind: "deleted" };
|
|
190
|
+
if (!isOk(answer.status))
|
|
191
|
+
return { kind: "failed", detail: errorDetail(answer.status, answer.body) };
|
|
192
|
+
const resource = answer.body?.data;
|
|
193
|
+
if (!resource || typeof resource !== "object" || Array.isArray(resource))
|
|
194
|
+
return { kind: "deleted" };
|
|
195
|
+
return { kind: "found", resource, document: answer.body };
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Relationship names the record's GET always answers when it has a link (the serialiser
|
|
199
|
+
* emits every descriptor relationship and omits only an empty one), so an omitted one is empty.
|
|
200
|
+
*/
|
|
201
|
+
knownRelationships(type) {
|
|
202
|
+
const descriptor = type ? entity_descriptor_registry_1.entityDescriptorRegistry.get(type) : undefined;
|
|
203
|
+
return Object.entries(descriptor?.relationships ?? {}).map(([name, rel]) => rel?.dtoKey ?? name);
|
|
204
|
+
}
|
|
205
|
+
mergeResolution(change) {
|
|
206
|
+
const resolution = change.resolution;
|
|
207
|
+
if (!resolution)
|
|
208
|
+
return undefined;
|
|
209
|
+
const merged = resolution.mergedBody?.data;
|
|
210
|
+
return {
|
|
211
|
+
choices: resolution.choices,
|
|
212
|
+
all: resolution.kind === "mine" ? "mine" : undefined,
|
|
213
|
+
merged: merged && typeof merged === "object" ? merged : undefined,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async applyEdit(change, request) {
|
|
217
|
+
const current = await this.readCurrent(change, request);
|
|
218
|
+
if (current.kind === "deleted")
|
|
219
|
+
return { seq: change.seq, status: "conflict", conflict: "deleted-on-server", current: null };
|
|
220
|
+
if (current.kind === "failed")
|
|
221
|
+
return { seq: change.seq, status: "could-not-apply", detail: current.detail };
|
|
222
|
+
const body = (change.body ?? {});
|
|
223
|
+
const mine = body.data ?? {};
|
|
224
|
+
const theirs = current.resource;
|
|
225
|
+
const base = change.base ?? theirs;
|
|
226
|
+
const merge = (0, three_way_merge_1.threeWayMerge)({
|
|
227
|
+
base,
|
|
228
|
+
mine,
|
|
229
|
+
theirs,
|
|
230
|
+
richTextFields: entity_descriptor_registry_1.entityDescriptorRegistry.richTextFields(change.recordType),
|
|
231
|
+
knownRelationships: this.knownRelationships(change.recordType),
|
|
232
|
+
resolution: this.mergeResolution(change),
|
|
233
|
+
});
|
|
234
|
+
if (merge.conflictFields.length > 0)
|
|
235
|
+
return {
|
|
236
|
+
seq: change.seq,
|
|
237
|
+
status: "conflict",
|
|
238
|
+
conflict: "fields",
|
|
239
|
+
current: theirs,
|
|
240
|
+
merged: merge.attributes,
|
|
241
|
+
conflictFields: merge.conflictFields,
|
|
242
|
+
conflictBlocks: merge.conflictBlocks,
|
|
243
|
+
};
|
|
244
|
+
if (merge.changed.length === 0)
|
|
245
|
+
return { seq: change.seq, status: "applied", response: current.document };
|
|
246
|
+
// The device's model-built document (decision "1a"), trimmed in place: fields the merge says
|
|
247
|
+
// nobody needs to write are removed, merged values (rich text included) replace the device's.
|
|
248
|
+
// Nothing is rebuilt: type, id and every other member stay as the device's model wrote them.
|
|
249
|
+
const trimmed = structuredClone(body);
|
|
250
|
+
const data = trimmed.data;
|
|
251
|
+
for (const name of Object.keys(data.attributes ?? {})) {
|
|
252
|
+
if (merge.changed.includes(name))
|
|
253
|
+
data.attributes[name] = merge.attributes[name];
|
|
254
|
+
else
|
|
255
|
+
delete data.attributes[name];
|
|
256
|
+
}
|
|
257
|
+
for (const name of Object.keys(data.relationships ?? {})) {
|
|
258
|
+
if (merge.changed.includes(name) && name in merge.relationships)
|
|
259
|
+
data.relationships[name] = merge.relationships[name];
|
|
260
|
+
else
|
|
261
|
+
delete data.relationships[name];
|
|
262
|
+
}
|
|
263
|
+
if (data.relationships && Object.keys(data.relationships).length === 0)
|
|
264
|
+
delete data.relationships;
|
|
265
|
+
const answer = await this.runner.run({ method: "PATCH", path: change.path, body: trimmed, request });
|
|
266
|
+
return this.toResult(change.seq, answer);
|
|
267
|
+
}
|
|
268
|
+
async applyDelete(change, request) {
|
|
269
|
+
if (!change.resolution) {
|
|
270
|
+
const current = await this.readCurrent(change, request);
|
|
271
|
+
if (current.kind === "deleted")
|
|
272
|
+
return { seq: change.seq, status: "applied" };
|
|
273
|
+
if (current.kind === "failed")
|
|
274
|
+
return { seq: change.seq, status: "could-not-apply", detail: current.detail };
|
|
275
|
+
if (change.base && this.changedSince(change.recordType, change.base, current.resource))
|
|
276
|
+
return { seq: change.seq, status: "conflict", conflict: "changed-on-server", current: current.resource };
|
|
277
|
+
}
|
|
278
|
+
const answer = await this.runner.run({ method: "DELETE", path: change.path, body: change.body, request });
|
|
279
|
+
// Already gone: what the GM asked for holds.
|
|
280
|
+
if (answer.ran === true && answer.status === 404)
|
|
281
|
+
return { seq: change.seq, status: "applied" };
|
|
282
|
+
return this.toResult(change.seq, answer);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* The attributes a change could have touched: the descriptor's fields that go on the wire as
|
|
286
|
+
* stored (no transform, such as a signed URL; not meta; not excluded), minus createdAt/updatedAt
|
|
287
|
+
* and the fields only the server writes (`SERVER_WRITTEN_ATTRIBUTES`, `immutable` fields).
|
|
288
|
+
* `undefined` for an unknown type (every attribute base holds is compared, minus those names).
|
|
289
|
+
*/
|
|
290
|
+
comparableAttributes(type) {
|
|
291
|
+
const descriptor = entity_descriptor_registry_1.entityDescriptorRegistry.get(type);
|
|
292
|
+
if (!descriptor)
|
|
293
|
+
return undefined;
|
|
294
|
+
const names = Object.entries(descriptor.fields ?? {})
|
|
295
|
+
.filter(([name, def]) => !VOLATILE_ATTRIBUTES.has(name) &&
|
|
296
|
+
!SERVER_WRITTEN_ATTRIBUTES.has(name) &&
|
|
297
|
+
!def?.immutable &&
|
|
298
|
+
!def?.transform &&
|
|
299
|
+
!def?.meta &&
|
|
300
|
+
!def?.excludeFromJsonApi &&
|
|
301
|
+
def?.serialise !== false)
|
|
302
|
+
.map(([name]) => name);
|
|
303
|
+
return new Set(names);
|
|
304
|
+
}
|
|
305
|
+
/** A descriptor field or a linked id of the record differs from `base`. */
|
|
306
|
+
changedSince(type, base, theirs) {
|
|
307
|
+
const comparable = this.comparableAttributes(type);
|
|
308
|
+
for (const [name, value] of Object.entries(base.attributes ?? {})) {
|
|
309
|
+
if (comparable ? !comparable.has(name) : VOLATILE_ATTRIBUTES.has(name) || SERVER_WRITTEN_ATTRIBUTES.has(name))
|
|
310
|
+
continue;
|
|
311
|
+
if (!(0, deep_equal_1.jsonEqual)(value, theirs.attributes?.[name]))
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
const known = new Set(this.knownRelationships(type));
|
|
315
|
+
for (const [name, rel] of Object.entries(base.relationships ?? {})) {
|
|
316
|
+
const baseIds = linkageIds(rel);
|
|
317
|
+
const theirsRel = theirs.relationships?.[name] ?? (known.has(name) ? { data: null } : undefined);
|
|
318
|
+
const theirsIds = linkageIds(theirsRel);
|
|
319
|
+
if (baseIds !== undefined && theirsIds !== undefined && !(0, deep_equal_1.jsonEqual)(baseIds, theirsIds))
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
exports.OfflineChangeSetService = OfflineChangeSetService;
|
|
326
|
+
exports.OfflineChangeSetService = OfflineChangeSetService = OfflineChangeSetService_1 = __decorate([
|
|
327
|
+
(0, common_1.Injectable)(),
|
|
328
|
+
__metadata("design:paramtypes", [jsonapi_service_1.JsonApiService,
|
|
329
|
+
offline_change_set_repository_1.OfflineChangeSetRepository,
|
|
330
|
+
nestjs_cls_1.ClsService,
|
|
331
|
+
offline_route_runner_service_1.OfflineRouteRunner])
|
|
332
|
+
], OfflineChangeSetService);
|
|
333
|
+
//# sourceMappingURL=offline-change-set.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-change-set.service.js","sourceRoot":"","sources":["../../../../src/foundations/offline-change-set/services/offline-change-set.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { DiscoveryService, HttpAdapterHost, MetadataScanner, Reflector } from "@nestjs/core";
|
|
2
|
+
import type { FastifyRequest } from "fastify";
|
|
3
|
+
/**
|
|
4
|
+
* The per-process token every step of an offline change set carries in `x-offline-internal`.
|
|
5
|
+
* Random, generated on first use, never leaves the process: only requests this runner injects
|
|
6
|
+
* carry it, so the throttler can tell them from client requests (one package = one request).
|
|
7
|
+
*/
|
|
8
|
+
export declare function offlineInternalToken(): string;
|
|
9
|
+
/** True when the headers carry this process's `x-offline-internal` token. */
|
|
10
|
+
export declare function isOfflineInternalRequest(headers: Record<string, unknown> | undefined): boolean;
|
|
11
|
+
export type OfflineRouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
12
|
+
/** `ran: false` = refused without running (the route is not marked `@OfflineReplayable()`). */
|
|
13
|
+
export type OfflineRouteAnswer = {
|
|
14
|
+
ran: true;
|
|
15
|
+
status: number;
|
|
16
|
+
body: any;
|
|
17
|
+
} | {
|
|
18
|
+
ran: false;
|
|
19
|
+
detail: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Runs one step of an offline change set through the app's own route with Fastify `inject`,
|
|
23
|
+
* as the caller: the caller's `Authorization`, `x-language` and `x-companyid` headers, plus
|
|
24
|
+
* `x-offline-replay: 1` and the process token. Every read and write therefore goes through
|
|
25
|
+
* the app's controller → service → repository, with its guards, validation and checks.
|
|
26
|
+
*
|
|
27
|
+
* A write (POST/PUT/PATCH/DELETE) runs only when the route that would answer it carries
|
|
28
|
+
* `@OfflineReplayable()`; any other write is refused without running. A GET (reading the
|
|
29
|
+
* current record before a merge) needs no marker: it changes nothing.
|
|
30
|
+
*
|
|
31
|
+
* Infrastructure provider: it owns no entity and touches no Neo4j itself.
|
|
32
|
+
*/
|
|
33
|
+
export declare class OfflineRouteRunner {
|
|
34
|
+
private readonly adapterHost;
|
|
35
|
+
private readonly discovery;
|
|
36
|
+
private readonly metadataScanner;
|
|
37
|
+
private readonly reflector;
|
|
38
|
+
private routes;
|
|
39
|
+
constructor(adapterHost: HttpAdapterHost, discovery: DiscoveryService, metadataScanner: MetadataScanner, reflector: Reflector);
|
|
40
|
+
run(params: {
|
|
41
|
+
method: OfflineRouteMethod;
|
|
42
|
+
path: string;
|
|
43
|
+
body?: unknown;
|
|
44
|
+
request: FastifyRequest;
|
|
45
|
+
}): Promise<OfflineRouteAnswer>;
|
|
46
|
+
/**
|
|
47
|
+
* `npcs/n1`, `/npcs/n1` or an absolute URL → `/npcs/n1` (query string kept), with dot segments
|
|
48
|
+
* resolved the way the router resolves them, so the route checked is the route that runs.
|
|
49
|
+
* `undefined` (refused) for a path that climbs above the root, or that carries an encoded dot
|
|
50
|
+
* or separator (`%2e`, `%2f`, `%5c`), which a later decode could turn into another path.
|
|
51
|
+
*/
|
|
52
|
+
private normalise;
|
|
53
|
+
private parse;
|
|
54
|
+
/**
|
|
55
|
+
* The route that answers `method path`, with the router's precedence: a static segment
|
|
56
|
+
* beats a parameter, a parameter beats a wildcard, compared from the first segment.
|
|
57
|
+
*/
|
|
58
|
+
private match;
|
|
59
|
+
private knownRoutes;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=offline-route-runner.service.d.ts.map
|