@statezero/core 0.2.38 → 0.2.40
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/actions/backend1/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/backend1/django_app/calculate-hash.js +80 -0
- package/dist/actions/backend1/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/backend1/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/backend1/django_app/get-current-username.js +65 -0
- package/dist/actions/backend1/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/backend1/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/backend1/django_app/get-server-status.js +68 -0
- package/dist/actions/backend1/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/backend1/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/backend1/django_app/get-user-info.js +70 -0
- package/dist/actions/backend1/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/backend1/django_app/index.d.ts +1 -0
- package/dist/actions/backend1/django_app/index.js +6 -0
- package/dist/actions/backend1/django_app/process-data.d.ts +51 -0
- package/dist/actions/backend1/django_app/process-data.js +78 -0
- package/dist/actions/backend1/django_app/process-data.schema.json +117 -0
- package/dist/actions/backend1/django_app/send-notification.d.ts +55 -0
- package/dist/actions/backend1/django_app/send-notification.js +81 -0
- package/dist/actions/backend1/django_app/send-notification.schema.json +175 -0
- package/dist/actions/backend1/index.d.ts +1 -0
- package/dist/actions/backend1/index.js +1 -0
- package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/default/django_app/calculate-hash.js +80 -0
- package/dist/actions/default/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/default/django_app/get-current-username.js +65 -0
- package/dist/actions/default/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/default/django_app/get-server-status.js +68 -0
- package/dist/actions/default/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/default/django_app/get-user-info.js +70 -0
- package/dist/actions/default/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/default/django_app/index.d.ts +1 -0
- package/dist/actions/default/django_app/index.js +6 -0
- package/dist/actions/default/django_app/process-data.d.ts +51 -0
- package/dist/actions/default/django_app/process-data.js +78 -0
- package/dist/actions/default/django_app/process-data.schema.json +117 -0
- package/dist/actions/default/django_app/send-notification.d.ts +55 -0
- package/dist/actions/default/django_app/send-notification.js +81 -0
- package/dist/actions/default/django_app/send-notification.schema.json +175 -0
- package/dist/actions/default/index.d.ts +1 -0
- package/dist/actions/default/index.js +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/adaptors/react/composables.d.ts +1 -0
- package/dist/adaptors/react/composables.js +4 -0
- package/dist/adaptors/react/index.d.ts +1 -0
- package/dist/adaptors/react/index.js +1 -0
- package/dist/adaptors/vue/components/LayoutRenderer.js +46 -49
- package/dist/adaptors/vue/components/defaults/index.d.ts +7 -0
- package/dist/adaptors/vue/components/defaults/index.js +31 -0
- package/dist/adaptors/vue/components/index.d.ts +1 -0
- package/dist/adaptors/vue/components/index.js +7 -0
- package/dist/adaptors/vue/composables.d.ts +2 -0
- package/dist/adaptors/vue/composables.js +44 -0
- package/dist/adaptors/vue/index.d.ts +3 -0
- package/dist/adaptors/vue/index.js +4 -0
- package/dist/adaptors/vue/reactivity.d.ts +18 -0
- package/dist/adaptors/vue/reactivity.js +132 -0
- package/dist/cli/commands/sync.d.ts +6 -0
- package/dist/cli/commands/sync.js +30 -0
- package/dist/cli/commands/syncActions.d.ts +46 -0
- package/dist/cli/commands/syncActions.js +717 -0
- package/dist/cli/commands/syncModels.d.ts +132 -0
- package/dist/cli/commands/syncModels.js +1120 -0
- package/dist/cli/configFileLoader.d.ts +10 -0
- package/dist/cli/configFileLoader.js +85 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +22 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.js +273 -0
- package/dist/core/eventReceivers.d.ts +185 -0
- package/dist/core/eventReceivers.js +266 -0
- package/dist/core/utils.d.ts +8 -0
- package/dist/core/utils.js +62 -0
- package/dist/errorHandler.d.ts +21 -0
- package/dist/errorHandler.js +27 -0
- package/dist/filtering/localFiltering.d.ts +110 -0
- package/dist/filtering/localFiltering.js +1080 -0
- package/dist/flavours/django/dates.d.ts +34 -0
- package/dist/flavours/django/dates.js +113 -0
- package/dist/flavours/django/errors.d.ts +138 -0
- package/dist/flavours/django/errors.js +195 -0
- package/dist/flavours/django/f.d.ts +6 -0
- package/dist/flavours/django/f.js +91 -0
- package/dist/flavours/django/files.d.ts +62 -0
- package/dist/flavours/django/files.js +355 -0
- package/dist/flavours/django/makeApiCall.d.ts +36 -0
- package/dist/flavours/django/makeApiCall.js +169 -0
- package/dist/flavours/django/manager.d.ts +204 -0
- package/dist/flavours/django/manager.js +222 -0
- package/dist/flavours/django/model.d.ts +137 -0
- package/dist/flavours/django/model.js +366 -0
- package/dist/flavours/django/operationFactory.d.ts +73 -0
- package/dist/flavours/django/operationFactory.js +248 -0
- package/dist/flavours/django/q.d.ts +70 -0
- package/dist/flavours/django/q.js +43 -0
- package/dist/flavours/django/queryExecutor.d.ts +149 -0
- package/dist/flavours/django/queryExecutor.js +590 -0
- package/dist/flavours/django/querySet.d.ts +301 -0
- package/dist/flavours/django/querySet.js +736 -0
- package/dist/flavours/django/serializers.d.ts +39 -0
- package/dist/flavours/django/serializers.js +296 -0
- package/dist/flavours/django/tempPk.d.ts +31 -0
- package/dist/flavours/django/tempPk.js +92 -0
- package/dist/flavours/django/utils.d.ts +19 -0
- package/dist/flavours/django/utils.js +29 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +48 -0
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/backend1/django_app/comprehensivemodel.js +71 -0
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/backend1/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/custompkmodel.js +69 -0
- package/dist/models/backend1/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/dailyrate.d.ts +230 -0
- package/dist/models/backend1/django_app/dailyrate.js +71 -0
- package/dist/models/backend1/django_app/dailyrate.schema.json +212 -0
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +140 -0
- package/dist/models/backend1/django_app/deepmodellevel1.js +72 -0
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +114 -0
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/deepmodellevel2.js +71 -0
- package/dist/models/backend1/django_app/deepmodellevel2.schema.json +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.js +69 -0
- package/dist/models/backend1/django_app/deepmodellevel3.schema.json +69 -0
- package/dist/models/backend1/django_app/dummymodel.d.ts +134 -0
- package/dist/models/backend1/django_app/dummymodel.js +71 -0
- package/dist/models/backend1/django_app/dummymodel.schema.json +109 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +69 -0
- package/dist/models/backend1/django_app/filetest.d.ts +140 -0
- package/dist/models/backend1/django_app/filetest.js +69 -0
- package/dist/models/backend1/django_app/filetest.schema.json +111 -0
- package/dist/models/backend1/django_app/index.d.ts +1 -0
- package/dist/models/backend1/django_app/index.js +21 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/order.d.ts +220 -0
- package/dist/models/backend1/django_app/order.js +71 -0
- package/dist/models/backend1/django_app/order.schema.json +203 -0
- package/dist/models/backend1/django_app/orderitem.d.ts +172 -0
- package/dist/models/backend1/django_app/orderitem.js +72 -0
- package/dist/models/backend1/django_app/orderitem.schema.json +149 -0
- package/dist/models/backend1/django_app/product.d.ts +254 -0
- package/dist/models/backend1/django_app/product.js +71 -0
- package/dist/models/backend1/django_app/product.schema.json +277 -0
- package/dist/models/backend1/django_app/productcategory.d.ts +92 -0
- package/dist/models/backend1/django_app/productcategory.js +69 -0
- package/dist/models/backend1/django_app/productcategory.schema.json +70 -0
- package/dist/models/backend1/django_app/rateplan.d.ts +92 -0
- package/dist/models/backend1/django_app/rateplan.js +69 -0
- package/dist/models/backend1/django_app/rateplan.schema.json +70 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/backend1/fileobject.d.ts +4 -0
- package/dist/models/backend1/fileobject.js +9 -0
- package/dist/models/backend1/index.d.ts +2 -0
- package/dist/models/backend1/index.js +2 -0
- package/dist/models/default/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/default/django_app/comprehensivemodel.js +71 -0
- package/dist/models/default/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/default/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/custompkmodel.js +69 -0
- package/dist/models/default/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/dailyrate.d.ts +230 -0
- package/dist/models/default/django_app/dailyrate.js +71 -0
- package/dist/models/default/django_app/dailyrate.schema.json +212 -0
- package/dist/models/default/django_app/deepmodellevel1.d.ts +128 -0
- package/dist/models/default/django_app/deepmodellevel1.js +72 -0
- package/dist/models/default/django_app/deepmodellevel1.schema.json +102 -0
- package/dist/models/default/django_app/deepmodellevel2.d.ts +106 -0
- package/dist/models/default/django_app/deepmodellevel2.js +71 -0
- package/dist/models/default/django_app/deepmodellevel2.schema.json +80 -0
- package/dist/models/default/django_app/deepmodellevel3.d.ts +80 -0
- package/dist/models/default/django_app/deepmodellevel3.js +69 -0
- package/dist/models/default/django_app/deepmodellevel3.schema.json +57 -0
- package/dist/models/default/django_app/dummymodel.d.ts +122 -0
- package/dist/models/default/django_app/dummymodel.js +71 -0
- package/dist/models/default/django_app/dummymodel.schema.json +97 -0
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +80 -0
- package/dist/models/default/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +57 -0
- package/dist/models/default/django_app/filetest.d.ts +128 -0
- package/dist/models/default/django_app/filetest.js +69 -0
- package/dist/models/default/django_app/filetest.schema.json +99 -0
- package/dist/models/default/django_app/index.d.ts +1 -0
- package/dist/models/default/django_app/index.js +21 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/order.d.ts +220 -0
- package/dist/models/default/django_app/order.js +71 -0
- package/dist/models/default/django_app/order.schema.json +203 -0
- package/dist/models/default/django_app/orderitem.d.ts +172 -0
- package/dist/models/default/django_app/orderitem.js +72 -0
- package/dist/models/default/django_app/orderitem.schema.json +149 -0
- package/dist/models/default/django_app/product.d.ts +254 -0
- package/dist/models/default/django_app/product.js +71 -0
- package/dist/models/default/django_app/product.schema.json +277 -0
- package/dist/models/default/django_app/productcategory.d.ts +92 -0
- package/dist/models/default/django_app/productcategory.js +69 -0
- package/dist/models/default/django_app/productcategory.schema.json +70 -0
- package/dist/models/default/django_app/rateplan.d.ts +92 -0
- package/dist/models/default/django_app/rateplan.js +69 -0
- package/dist/models/default/django_app/rateplan.schema.json +70 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/default/fileobject.d.ts +4 -0
- package/dist/models/default/fileobject.js +9 -0
- package/dist/models/default/index.d.ts +2 -0
- package/dist/models/default/index.js +2 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +5 -0
- package/dist/react-entry.d.ts +2 -0
- package/dist/react-entry.js +2 -0
- package/dist/reactiveAdaptor.d.ts +24 -0
- package/dist/reactiveAdaptor.js +38 -0
- package/dist/reset.d.ts +15 -0
- package/dist/reset.js +97 -0
- package/dist/setup.d.ts +15 -0
- package/dist/setup.js +33 -0
- package/dist/syncEngine/cache/cache.d.ts +75 -0
- package/dist/syncEngine/cache/cache.js +355 -0
- package/dist/syncEngine/metrics/metricOptCalcs.d.ts +79 -0
- package/dist/syncEngine/metrics/metricOptCalcs.js +284 -0
- package/dist/syncEngine/registries/metricRegistry.d.ts +58 -0
- package/dist/syncEngine/registries/metricRegistry.js +171 -0
- package/dist/syncEngine/registries/modelStoreRegistry.d.ts +11 -0
- package/dist/syncEngine/registries/modelStoreRegistry.js +63 -0
- package/dist/syncEngine/registries/querysetStoreGraph.d.ts +41 -0
- package/dist/syncEngine/registries/querysetStoreGraph.js +174 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.d.ts +72 -0
- package/dist/syncEngine/registries/querysetStoreRegistry.js +335 -0
- package/dist/syncEngine/stores/metricStore.d.ts +55 -0
- package/dist/syncEngine/stores/metricStore.js +222 -0
- package/dist/syncEngine/stores/modelStore.d.ts +53 -0
- package/dist/syncEngine/stores/modelStore.js +565 -0
- package/dist/syncEngine/stores/operation.d.ts +139 -0
- package/dist/syncEngine/stores/operation.js +291 -0
- package/dist/syncEngine/stores/operationEventHandlers.d.ts +8 -0
- package/dist/syncEngine/stores/operationEventHandlers.js +348 -0
- package/dist/syncEngine/stores/querysetStore.d.ts +60 -0
- package/dist/syncEngine/stores/querysetStore.js +294 -0
- package/dist/syncEngine/stores/reactivity.d.ts +3 -0
- package/dist/syncEngine/stores/reactivity.js +4 -0
- package/dist/syncEngine/stores/utils.d.ts +14 -0
- package/dist/syncEngine/stores/utils.js +32 -0
- package/dist/syncEngine/sync.d.ts +51 -0
- package/dist/syncEngine/sync.js +419 -0
- package/dist/testing.d.ts +63 -0
- package/dist/testing.js +175 -0
- package/dist/vue-entry.d.ts +15 -0
- package/dist/vue-entry.js +7 -0
- package/package.json +6 -7
- package/dist/adaptors/vue/components/layout.tailwind.css +0 -51
- /package/{dist → src}/adaptors/vue/components/layout.css +0 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { Manager } from "./manager.js";
|
|
2
|
+
import { ValidationError } from "./errors.js";
|
|
3
|
+
import { modelStoreRegistry } from "../../syncEngine/registries/modelStoreRegistry.js";
|
|
4
|
+
import { isNil } from "lodash-es";
|
|
5
|
+
import { QueryExecutor } from "./queryExecutor.js";
|
|
6
|
+
import { wrapReactiveModel } from "../../reactiveAdaptor.js";
|
|
7
|
+
import { DateParsingHelpers } from "./dates.js";
|
|
8
|
+
import { FileObject } from './files.js';
|
|
9
|
+
import { configInstance } from "../../config.js";
|
|
10
|
+
import { ModelSerializer } from "./serializers.js";
|
|
11
|
+
import { parseStateZeroError, MultipleObjectsReturned, DoesNotExist, } from "./errors.js";
|
|
12
|
+
import axios from "axios";
|
|
13
|
+
/**
|
|
14
|
+
* A constructor for a Model.
|
|
15
|
+
*
|
|
16
|
+
* @typedef {Function} ModelConstructor
|
|
17
|
+
* @param {any} data - Data to initialize the model.
|
|
18
|
+
* @returns {Model}
|
|
19
|
+
*
|
|
20
|
+
* @property {Manager} objects - The model's manager.
|
|
21
|
+
* @property {string} configKey - The configuration key.
|
|
22
|
+
* @property {string} modelName - The model name.
|
|
23
|
+
* @property {string} primaryKeyField - The primary key field (default 'id').
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Base Model class with integrated API implementation.
|
|
27
|
+
*
|
|
28
|
+
* @abstract
|
|
29
|
+
*/
|
|
30
|
+
export class Model {
|
|
31
|
+
constructor(data = {}) {
|
|
32
|
+
this.serializer = new ModelSerializer(this.constructor);
|
|
33
|
+
const serializedData = this.serializer.toInternal(data);
|
|
34
|
+
this._data = serializedData;
|
|
35
|
+
this._pk = serializedData[this.constructor.primaryKeyField] || undefined;
|
|
36
|
+
this.__version = 0;
|
|
37
|
+
return wrapReactiveModel(this);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Remote-only manager that skips local store updates.
|
|
41
|
+
*/
|
|
42
|
+
static get remote() {
|
|
43
|
+
return this.objects.remote();
|
|
44
|
+
}
|
|
45
|
+
touch() {
|
|
46
|
+
this.__version++;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns the primary key of the model instance.
|
|
50
|
+
*
|
|
51
|
+
* @returns {number|undefined} The primary key.
|
|
52
|
+
*/
|
|
53
|
+
get pk() {
|
|
54
|
+
return this._pk;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sets the primary key of the model instance.
|
|
58
|
+
*
|
|
59
|
+
* @param {number|undefined} value - The new primary key value.
|
|
60
|
+
*/
|
|
61
|
+
set pk(value) {
|
|
62
|
+
this._pk = value;
|
|
63
|
+
this.touch();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Instantiate from pk using queryset scoped singletons
|
|
67
|
+
*/
|
|
68
|
+
static fromPk(pk, querySet) {
|
|
69
|
+
let qsId = querySet ? querySet.__uuid : "";
|
|
70
|
+
let key = `${qsId}__${this.configKey}__${this.modelName}__${pk}`;
|
|
71
|
+
if (!this.instanceCache.has(key)) {
|
|
72
|
+
const instance = new this();
|
|
73
|
+
instance.pk = pk;
|
|
74
|
+
this.instanceCache.set(key, instance);
|
|
75
|
+
}
|
|
76
|
+
return this.instanceCache.get(key);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gets a field value from the internal data store
|
|
80
|
+
*
|
|
81
|
+
* @param {string} field - The field name
|
|
82
|
+
* @returns {any} The field value
|
|
83
|
+
*/
|
|
84
|
+
getField(field) {
|
|
85
|
+
// Access the reactive __version property to establish dependency for vue integration
|
|
86
|
+
const trackVersion = this.__version;
|
|
87
|
+
const ModelClass = this.constructor;
|
|
88
|
+
if (ModelClass.primaryKeyField === field)
|
|
89
|
+
return this._pk;
|
|
90
|
+
// check local overrides
|
|
91
|
+
let value = this._data[field];
|
|
92
|
+
// if its not been overridden, get it from the store
|
|
93
|
+
if (value === undefined && !isNil(this._pk)) {
|
|
94
|
+
let storedValue = modelStoreRegistry.getEntity(ModelClass, this._pk);
|
|
95
|
+
if (storedValue)
|
|
96
|
+
value = storedValue[field]; // if stops null -> undefined
|
|
97
|
+
}
|
|
98
|
+
// Use serializer to convert internal format to live format
|
|
99
|
+
return this.serializer.toLiveField(field, value);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sets a field value in the internal data store
|
|
103
|
+
*
|
|
104
|
+
* @param {string} field - The field name
|
|
105
|
+
* @param {any} value - The field value to set
|
|
106
|
+
*/
|
|
107
|
+
setField(field, value) {
|
|
108
|
+
const ModelClass = this.constructor;
|
|
109
|
+
// Use serializer to convert live format to internal format
|
|
110
|
+
const internalValue = this.serializer.toInternalField(field, value);
|
|
111
|
+
if (ModelClass.primaryKeyField === field) {
|
|
112
|
+
this._pk = internalValue;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
this._data[field] = internalValue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validates that the provided data object only contains keys
|
|
120
|
+
* defined in the model's allowed fields. Supports nested fields
|
|
121
|
+
* using double underscore notation (e.g., author__name).
|
|
122
|
+
*
|
|
123
|
+
* @param {Object} data - The object to validate.
|
|
124
|
+
* @throws {ValidationError} If an unknown key is found.
|
|
125
|
+
*/
|
|
126
|
+
static validateFields(data) {
|
|
127
|
+
if (isNil(data))
|
|
128
|
+
return;
|
|
129
|
+
const allowedFields = this.fields;
|
|
130
|
+
for (const key of Object.keys(data)) {
|
|
131
|
+
if (key === "repr" || key === "type")
|
|
132
|
+
continue;
|
|
133
|
+
// Handle nested fields by splitting on double underscore
|
|
134
|
+
// and taking just the base field name
|
|
135
|
+
const baseField = key.split("__")[0];
|
|
136
|
+
if (!allowedFields.includes(baseField)) {
|
|
137
|
+
let errorMsg = `Invalid field: ${baseField}. Allowed fields are: ${allowedFields.join(", ")}`;
|
|
138
|
+
console.error(errorMsg);
|
|
139
|
+
throw new ValidationError(errorMsg);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Serializes the model instance.
|
|
145
|
+
*
|
|
146
|
+
* By default, it returns all enumerable own properties.
|
|
147
|
+
* Subclasses should override this to return specific keys.
|
|
148
|
+
*
|
|
149
|
+
* @param {boolean} includeRepr - Whether to include the repr field (for caching). Default: false.
|
|
150
|
+
* @returns {Object} The serialized model data.
|
|
151
|
+
*/
|
|
152
|
+
serialize(includeRepr = false) {
|
|
153
|
+
const ModelClass = this.constructor;
|
|
154
|
+
const data = {};
|
|
155
|
+
// Collect all field values (already in internal format)
|
|
156
|
+
for (const field of ModelClass.fields) {
|
|
157
|
+
if (field === ModelClass.primaryKeyField) {
|
|
158
|
+
data[field] = this._pk;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
let value = this._data[field];
|
|
162
|
+
// Get from store if not in local data
|
|
163
|
+
if (value === undefined && !isNil(this._pk)) {
|
|
164
|
+
const storedData = modelStoreRegistry.getEntity(ModelClass, this._pk);
|
|
165
|
+
if (storedData) {
|
|
166
|
+
value = storedData[field];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
data[field] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Include repr field if requested (for caching purposes)
|
|
173
|
+
if (includeRepr && !isNil(this._pk)) {
|
|
174
|
+
const storedData = modelStoreRegistry.getEntity(ModelClass, this._pk);
|
|
175
|
+
if (storedData && storedData.repr) {
|
|
176
|
+
data.repr = storedData.repr;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Data is already in internal format, so return as-is for API transmission
|
|
180
|
+
return data;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Saves the model instance by either creating a new record or updating an existing one.
|
|
184
|
+
*
|
|
185
|
+
* @returns {Promise<Model>} A promise that resolves to the updated model instance.
|
|
186
|
+
*/
|
|
187
|
+
async save() {
|
|
188
|
+
const ModelClass = this.constructor;
|
|
189
|
+
const pkField = ModelClass.primaryKeyField;
|
|
190
|
+
const querySet = !this.pk
|
|
191
|
+
? ModelClass.objects.newQuerySet()
|
|
192
|
+
: ModelClass.objects.filter({ [pkField]: this.pk });
|
|
193
|
+
const data = this.serialize();
|
|
194
|
+
let instance;
|
|
195
|
+
if (!this.pk) {
|
|
196
|
+
// Create new instance
|
|
197
|
+
instance = await QueryExecutor.execute(querySet, "create", { data });
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Update existing instance
|
|
201
|
+
instance = await QueryExecutor.execute(querySet, "update_instance", {
|
|
202
|
+
data,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
this._pk = instance.pk;
|
|
206
|
+
this._data = {};
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Deletes the instance from the database.
|
|
211
|
+
*
|
|
212
|
+
* Returns a tuple with the number of objects deleted and an object mapping
|
|
213
|
+
* model names to the number of objects deleted, matching Django's behavior.
|
|
214
|
+
*
|
|
215
|
+
* @returns {Promise<[number, Object]>} A promise that resolves to the deletion result.
|
|
216
|
+
* @throws {Error} If the instance has not been saved (no primary key).
|
|
217
|
+
*/
|
|
218
|
+
async delete() {
|
|
219
|
+
if (!this.pk) {
|
|
220
|
+
throw new Error("Cannot delete unsaved instance");
|
|
221
|
+
}
|
|
222
|
+
const ModelClass = this.constructor;
|
|
223
|
+
const pkField = ModelClass.primaryKeyField;
|
|
224
|
+
const querySet = ModelClass.objects.filter({ [pkField]: this.pk });
|
|
225
|
+
// Pass the instance data with primary key as the args
|
|
226
|
+
const args = { [pkField]: this.pk };
|
|
227
|
+
const result = await QueryExecutor.execute(querySet, "delete_instance", args);
|
|
228
|
+
// result -> [deletedCount, { [modelName]: deletedCount }];
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Refreshes the model instance with data from the database.
|
|
233
|
+
*
|
|
234
|
+
* @returns {Promise<void>} A promise that resolves when the instance has been refreshed.
|
|
235
|
+
* @throws {Error} If the instance has not been saved (no primary key).
|
|
236
|
+
*/
|
|
237
|
+
async refreshFromDb() {
|
|
238
|
+
if (!this.pk) {
|
|
239
|
+
throw new Error("Cannot refresh unsaved instance");
|
|
240
|
+
}
|
|
241
|
+
const ModelClass = this.constructor;
|
|
242
|
+
const fresh = await ModelClass.objects.get({
|
|
243
|
+
[ModelClass.primaryKeyField]: this.pk,
|
|
244
|
+
});
|
|
245
|
+
// clear the current data and fresh data will flow
|
|
246
|
+
this._data = {};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Validates the model instance using the same serialize behavior as save()
|
|
250
|
+
* @param {string} validateType - 'create' or 'update' (defaults to auto-detect)
|
|
251
|
+
* @param {boolean} partial - Whether to allow partial validation
|
|
252
|
+
* @returns {Promise<boolean>} Promise that resolves to true if valid, throws error if invalid
|
|
253
|
+
*/
|
|
254
|
+
async validate(validateType = null, partial = false) {
|
|
255
|
+
const ModelClass = this.constructor;
|
|
256
|
+
if (!validateType) {
|
|
257
|
+
validateType = this.pk ? "update" : "create";
|
|
258
|
+
}
|
|
259
|
+
// Validate the validateType parameter
|
|
260
|
+
if (!["update", "create"].includes(validateType)) {
|
|
261
|
+
throw new Error(`Validation type must be 'update' or 'create', not '${validateType}'`);
|
|
262
|
+
}
|
|
263
|
+
// Use the same serialize logic as save()
|
|
264
|
+
const data = this.serialize();
|
|
265
|
+
// Delegate to static method
|
|
266
|
+
return ModelClass.validate(data, validateType, partial);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Static method to validate data without creating an instance
|
|
270
|
+
* @param {Object} data - Data to validate
|
|
271
|
+
* @param {string} validateType - 'create' or 'update'
|
|
272
|
+
* @param {boolean} partial - Whether to allow partial validation
|
|
273
|
+
* @returns {Promise<boolean>} Promise that resolves to true if valid, throws error if invalid
|
|
274
|
+
*/
|
|
275
|
+
static async validate(data, validateType = "create", partial = false) {
|
|
276
|
+
const ModelClass = this;
|
|
277
|
+
// Validate the validateType parameter
|
|
278
|
+
if (!["update", "create"].includes(validateType)) {
|
|
279
|
+
throw new Error(`Validation type must be 'update' or 'create', not '${validateType}'`);
|
|
280
|
+
}
|
|
281
|
+
// Get backend config and check if it exists
|
|
282
|
+
const config = configInstance.getConfig();
|
|
283
|
+
const backend = config.backendConfigs[ModelClass.configKey];
|
|
284
|
+
if (!backend) {
|
|
285
|
+
throw new Error(`No backend configuration found for key: ${ModelClass.configKey}`);
|
|
286
|
+
}
|
|
287
|
+
// Build URL for validate endpoint
|
|
288
|
+
const baseUrl = backend.API_URL.replace(/\/+$/, "");
|
|
289
|
+
const url = `${baseUrl}/${ModelClass.modelName}/validate/`;
|
|
290
|
+
// Prepare headers
|
|
291
|
+
const headers = {
|
|
292
|
+
"Content-Type": "application/json",
|
|
293
|
+
...(backend.getAuthHeaders ? backend.getAuthHeaders() : {}),
|
|
294
|
+
};
|
|
295
|
+
// Make direct API call to validate endpoint
|
|
296
|
+
try {
|
|
297
|
+
const response = await axios.post(url, {
|
|
298
|
+
data: data,
|
|
299
|
+
validate_type: validateType,
|
|
300
|
+
partial: partial,
|
|
301
|
+
}, { headers });
|
|
302
|
+
// Backend returns {"valid": true} on success
|
|
303
|
+
return response.data.valid === true;
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
if (error.response && error.response.data) {
|
|
307
|
+
const parsedError = parseStateZeroError(error.response.data);
|
|
308
|
+
if (Error.captureStackTrace) {
|
|
309
|
+
Error.captureStackTrace(parsedError, ModelClass.validate);
|
|
310
|
+
}
|
|
311
|
+
throw parsedError;
|
|
312
|
+
}
|
|
313
|
+
throw new Error(`Validation failed: ${error.message}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get field permissions for the current user (cached on the class)
|
|
318
|
+
* @param {boolean} refresh - Force refresh the cached permissions
|
|
319
|
+
* @returns {Promise<{visible_fields: string[], creatable_fields: string[], editable_fields: string[]}>}
|
|
320
|
+
*/
|
|
321
|
+
static async getFieldPermissions(refresh = false) {
|
|
322
|
+
const ModelClass = this;
|
|
323
|
+
// Return cached permissions if available and not forcing refresh
|
|
324
|
+
if (!refresh && ModelClass._fieldPermissionsCache) {
|
|
325
|
+
return ModelClass._fieldPermissionsCache;
|
|
326
|
+
}
|
|
327
|
+
// Get backend config and check if it exists
|
|
328
|
+
const config = configInstance.getConfig();
|
|
329
|
+
const backend = config.backendConfigs[ModelClass.configKey];
|
|
330
|
+
if (!backend) {
|
|
331
|
+
throw new Error(`No backend configuration found for key: ${ModelClass.configKey}`);
|
|
332
|
+
}
|
|
333
|
+
// Build URL for field permissions endpoint
|
|
334
|
+
const baseUrl = backend.API_URL.replace(/\/+$/, "");
|
|
335
|
+
const url = `${baseUrl}/${ModelClass.modelName}/field-permissions/`;
|
|
336
|
+
// Prepare headers
|
|
337
|
+
const headers = {
|
|
338
|
+
"Content-Type": "application/json",
|
|
339
|
+
...(backend.getAuthHeaders ? backend.getAuthHeaders() : {}),
|
|
340
|
+
};
|
|
341
|
+
// Make direct API call to field permissions endpoint
|
|
342
|
+
try {
|
|
343
|
+
const response = await axios.get(url, { headers });
|
|
344
|
+
// Cache the permissions on the class
|
|
345
|
+
ModelClass._fieldPermissionsCache = response.data;
|
|
346
|
+
// Backend returns {visible_fields: [], creatable_fields: [], editable_fields: []}
|
|
347
|
+
return response.data;
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
if (error.response && error.response.data) {
|
|
351
|
+
const parsedError = parseStateZeroError(error.response.data);
|
|
352
|
+
if (Error.captureStackTrace) {
|
|
353
|
+
Error.captureStackTrace(parsedError, ModelClass.getFieldPermissions);
|
|
354
|
+
}
|
|
355
|
+
throw parsedError;
|
|
356
|
+
}
|
|
357
|
+
throw new Error(`Failed to get field permissions: ${error.message}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Creates a new Model instance.
|
|
363
|
+
*
|
|
364
|
+
* @param {any} [data={}] - The data for initialization.
|
|
365
|
+
*/
|
|
366
|
+
Model.instanceCache = new Map();
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating Operation instances with consistent behavior
|
|
3
|
+
* across QueryExecutor and hotpath event handling
|
|
4
|
+
*/
|
|
5
|
+
export class OperationFactory {
|
|
6
|
+
/**
|
|
7
|
+
* Create a CREATE operation
|
|
8
|
+
* @param {QuerySet} queryset - The queryset context
|
|
9
|
+
* @param {Object} data - The data for the new instance
|
|
10
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
11
|
+
* @returns {Operation} The created operation
|
|
12
|
+
*/
|
|
13
|
+
static createCreateOperation(queryset: QuerySet, data?: Object, operationId?: string): Operation;
|
|
14
|
+
/**
|
|
15
|
+
* Create a BULK_CREATE operation
|
|
16
|
+
* @param {QuerySet} queryset - The queryset context
|
|
17
|
+
* @param {Array<Object>} dataList - Array of data objects for the new instances
|
|
18
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
19
|
+
* @returns {Operation} The created operation
|
|
20
|
+
*/
|
|
21
|
+
static createBulkCreateOperation(queryset: QuerySet, dataList?: Array<Object>, operationId?: string): Operation;
|
|
22
|
+
/**
|
|
23
|
+
* Create an UPDATE operation with optimistic instance updates
|
|
24
|
+
* @param {QuerySet} queryset - The queryset context
|
|
25
|
+
* @param {Object} data - The update data
|
|
26
|
+
* @param {Object} filter - Optional filter for the update
|
|
27
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
28
|
+
* @returns {Operation} The created operation
|
|
29
|
+
*/
|
|
30
|
+
static createUpdateOperation(queryset: QuerySet, data?: Object, filter?: Object, operationId?: string): Operation;
|
|
31
|
+
/**
|
|
32
|
+
* Create a DELETE operation
|
|
33
|
+
* @param {QuerySet} queryset - The queryset context
|
|
34
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
35
|
+
* @returns {Operation} The created operation
|
|
36
|
+
*/
|
|
37
|
+
static createDeleteOperation(queryset: QuerySet, operationId?: string): Operation;
|
|
38
|
+
/**
|
|
39
|
+
* Create an UPDATE_INSTANCE operation
|
|
40
|
+
* @param {QuerySet} queryset - The queryset context
|
|
41
|
+
* @param {Object} data - The update data
|
|
42
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
43
|
+
* @returns {Operation} The created operation
|
|
44
|
+
*/
|
|
45
|
+
static createUpdateInstanceOperation(queryset: QuerySet, data?: Object, operationId?: string): Operation;
|
|
46
|
+
/**
|
|
47
|
+
* Create a DELETE_INSTANCE operation
|
|
48
|
+
* @param {QuerySet} queryset - The queryset context
|
|
49
|
+
* @param {Object} instanceData - The instance to delete (object with PK)
|
|
50
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
51
|
+
* @returns {Operation} The created operation
|
|
52
|
+
*/
|
|
53
|
+
static createDeleteInstanceOperation(queryset: QuerySet, instanceData: Object, operationId?: string): Operation;
|
|
54
|
+
/**
|
|
55
|
+
* Create a GET_OR_CREATE operation with local filtering logic
|
|
56
|
+
* @param {QuerySet} queryset - The queryset context
|
|
57
|
+
* @param {Object} lookup - The lookup criteria
|
|
58
|
+
* @param {Object} defaults - The default values for creation
|
|
59
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
60
|
+
* @returns {Operation} The created operation
|
|
61
|
+
*/
|
|
62
|
+
static createGetOrCreateOperation(queryset: QuerySet, lookup?: Object, defaults?: Object, operationId?: string): Operation;
|
|
63
|
+
/**
|
|
64
|
+
* Create an UPDATE_OR_CREATE operation with local filtering logic
|
|
65
|
+
* @param {QuerySet} queryset - The queryset context
|
|
66
|
+
* @param {Object} lookup - The lookup criteria
|
|
67
|
+
* @param {Object} defaults - The default values for creation/update
|
|
68
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
69
|
+
* @returns {Operation} The created operation
|
|
70
|
+
*/
|
|
71
|
+
static createUpdateOrCreateOperation(queryset: QuerySet, lookup?: Object, defaults?: Object, operationId?: string): Operation;
|
|
72
|
+
}
|
|
73
|
+
import { Operation } from '../../syncEngine/stores/operation.js';
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Operation, Type, Status } from '../../syncEngine/stores/operation.js';
|
|
2
|
+
import { v7 as uuid7 } from 'uuid';
|
|
3
|
+
import { createTempPk } from './tempPk.js';
|
|
4
|
+
import { getRequiredFields, pickRequiredFields, processQuery } from '../../filtering/localFiltering.js';
|
|
5
|
+
import { evaluateExpression } from './f.js';
|
|
6
|
+
import { modelStoreRegistry } from '../../syncEngine/registries/modelStoreRegistry.js';
|
|
7
|
+
import { querysetStoreRegistry } from '../../syncEngine/registries/querysetStoreRegistry.js';
|
|
8
|
+
import { isNil } from 'lodash-es';
|
|
9
|
+
/**
|
|
10
|
+
* Factory for creating Operation instances with consistent behavior
|
|
11
|
+
* across QueryExecutor and hotpath event handling
|
|
12
|
+
*/
|
|
13
|
+
export class OperationFactory {
|
|
14
|
+
/**
|
|
15
|
+
* Create a CREATE operation
|
|
16
|
+
* @param {QuerySet} queryset - The queryset context
|
|
17
|
+
* @param {Object} data - The data for the new instance
|
|
18
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
19
|
+
* @returns {Operation} The created operation
|
|
20
|
+
*/
|
|
21
|
+
static createCreateOperation(queryset, data = {}, operationId = null) {
|
|
22
|
+
const ModelClass = queryset.ModelClass;
|
|
23
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
24
|
+
const opId = operationId || `${uuid7()}`;
|
|
25
|
+
const tempPk = createTempPk(opId);
|
|
26
|
+
return new Operation({
|
|
27
|
+
operationId: opId,
|
|
28
|
+
type: Type.CREATE,
|
|
29
|
+
instances: [{ ...data, [primaryKeyField]: tempPk }],
|
|
30
|
+
queryset: queryset,
|
|
31
|
+
args: { data },
|
|
32
|
+
localOnly: queryset._optimisticOnly || false,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a BULK_CREATE operation
|
|
37
|
+
* @param {QuerySet} queryset - The queryset context
|
|
38
|
+
* @param {Array<Object>} dataList - Array of data objects for the new instances
|
|
39
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
40
|
+
* @returns {Operation} The created operation
|
|
41
|
+
*/
|
|
42
|
+
static createBulkCreateOperation(queryset, dataList = [], operationId = null) {
|
|
43
|
+
const ModelClass = queryset.ModelClass;
|
|
44
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
45
|
+
const opId = operationId || `${uuid7()}`;
|
|
46
|
+
// Create temp PKs for each instance
|
|
47
|
+
const instances = dataList.map((data, index) => {
|
|
48
|
+
const tempPk = createTempPk(`${opId}_${index}`);
|
|
49
|
+
return { ...data, [primaryKeyField]: tempPk };
|
|
50
|
+
});
|
|
51
|
+
return new Operation({
|
|
52
|
+
operationId: opId,
|
|
53
|
+
type: Type.BULK_CREATE,
|
|
54
|
+
instances: instances,
|
|
55
|
+
queryset: queryset,
|
|
56
|
+
args: { data: dataList },
|
|
57
|
+
localOnly: queryset._optimisticOnly || false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create an UPDATE operation with optimistic instance updates
|
|
62
|
+
* @param {QuerySet} queryset - The queryset context
|
|
63
|
+
* @param {Object} data - The update data
|
|
64
|
+
* @param {Object} filter - Optional filter for the update
|
|
65
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
66
|
+
* @returns {Operation} The created operation
|
|
67
|
+
*/
|
|
68
|
+
static createUpdateOperation(queryset, data = {}, filter = null, operationId = null) {
|
|
69
|
+
const ModelClass = queryset.ModelClass;
|
|
70
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
71
|
+
const store = querysetStoreRegistry.getStore(queryset);
|
|
72
|
+
const querysetPks = store.render();
|
|
73
|
+
const opId = operationId || `${uuid7()}`;
|
|
74
|
+
// Create optimistic instances with F expression evaluation
|
|
75
|
+
const optimisticInstances = querysetPks.map(pk => {
|
|
76
|
+
const instance = modelStoreRegistry.getEntity(ModelClass, pk);
|
|
77
|
+
const updatedInstance = { ...instance };
|
|
78
|
+
updatedInstance[primaryKeyField] = pk;
|
|
79
|
+
for (const [key, value] of Object.entries(data)) {
|
|
80
|
+
if (value && typeof value === 'object' && value.__f_expr) {
|
|
81
|
+
const evaluatedValue = evaluateExpression(value, instance);
|
|
82
|
+
if (evaluatedValue !== null) {
|
|
83
|
+
updatedInstance[key] = evaluatedValue;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
updatedInstance[key] = instance[key];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
updatedInstance[key] = value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return updatedInstance;
|
|
94
|
+
});
|
|
95
|
+
return new Operation({
|
|
96
|
+
operationId: opId,
|
|
97
|
+
type: Type.UPDATE,
|
|
98
|
+
instances: optimisticInstances,
|
|
99
|
+
queryset: queryset,
|
|
100
|
+
args: { filter, data },
|
|
101
|
+
localOnly: queryset._optimisticOnly || false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a DELETE operation
|
|
106
|
+
* @param {QuerySet} queryset - The queryset context
|
|
107
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
108
|
+
* @returns {Operation} The created operation
|
|
109
|
+
*/
|
|
110
|
+
static createDeleteOperation(queryset, operationId = null) {
|
|
111
|
+
const ModelClass = queryset.ModelClass;
|
|
112
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
113
|
+
const store = querysetStoreRegistry.getStore(queryset);
|
|
114
|
+
const querysetPks = store.render();
|
|
115
|
+
const opId = operationId || `${uuid7()}`;
|
|
116
|
+
const instances = querysetPks.map((pk) => ({ [primaryKeyField]: pk }));
|
|
117
|
+
return new Operation({
|
|
118
|
+
operationId: opId,
|
|
119
|
+
type: Type.DELETE,
|
|
120
|
+
instances: instances,
|
|
121
|
+
queryset: queryset,
|
|
122
|
+
args: {},
|
|
123
|
+
localOnly: queryset._optimisticOnly || false,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create an UPDATE_INSTANCE operation
|
|
128
|
+
* @param {QuerySet} queryset - The queryset context
|
|
129
|
+
* @param {Object} data - The update data
|
|
130
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
131
|
+
* @returns {Operation} The created operation
|
|
132
|
+
*/
|
|
133
|
+
static createUpdateInstanceOperation(queryset, data = {}, operationId = null) {
|
|
134
|
+
const ModelClass = queryset.ModelClass;
|
|
135
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
136
|
+
const store = querysetStoreRegistry.getStore(queryset);
|
|
137
|
+
const querysetPks = store.render();
|
|
138
|
+
const opId = operationId || `${uuid7()}`;
|
|
139
|
+
const instances = querysetPks.map(pk => ({ ...data, [primaryKeyField]: pk }));
|
|
140
|
+
return new Operation({
|
|
141
|
+
operationId: opId,
|
|
142
|
+
type: Type.UPDATE_INSTANCE,
|
|
143
|
+
instances: instances,
|
|
144
|
+
queryset: queryset,
|
|
145
|
+
args: { data },
|
|
146
|
+
localOnly: queryset._optimisticOnly || false,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create a DELETE_INSTANCE operation
|
|
151
|
+
* @param {QuerySet} queryset - The queryset context
|
|
152
|
+
* @param {Object} instanceData - The instance to delete (object with PK)
|
|
153
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
154
|
+
* @returns {Operation} The created operation
|
|
155
|
+
*/
|
|
156
|
+
static createDeleteInstanceOperation(queryset, instanceData, operationId = null) {
|
|
157
|
+
const opId = operationId || `${uuid7()}`;
|
|
158
|
+
return new Operation({
|
|
159
|
+
operationId: opId,
|
|
160
|
+
type: Type.DELETE_INSTANCE,
|
|
161
|
+
instances: [instanceData],
|
|
162
|
+
queryset: queryset,
|
|
163
|
+
args: instanceData,
|
|
164
|
+
localOnly: queryset._optimisticOnly || false,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create a GET_OR_CREATE operation with local filtering logic
|
|
169
|
+
* @param {QuerySet} queryset - The queryset context
|
|
170
|
+
* @param {Object} lookup - The lookup criteria
|
|
171
|
+
* @param {Object} defaults - The default values for creation
|
|
172
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
173
|
+
* @returns {Operation} The created operation
|
|
174
|
+
*/
|
|
175
|
+
static createGetOrCreateOperation(queryset, lookup = {}, defaults = {}, operationId = null) {
|
|
176
|
+
const ModelClass = queryset.ModelClass;
|
|
177
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
178
|
+
const opId = operationId || `${uuid7()}`;
|
|
179
|
+
// Get all current instances from the store for local filtering
|
|
180
|
+
const modelStore = modelStoreRegistry.getStore(ModelClass);
|
|
181
|
+
const allInstances = modelStore.render();
|
|
182
|
+
// Create a queryset filter for the lookup criteria
|
|
183
|
+
const lookupFilter = { ...lookup };
|
|
184
|
+
const lookupQuerySet = new queryset.constructor(ModelClass).filter(lookupFilter);
|
|
185
|
+
const lookupQuery = lookupQuerySet.build();
|
|
186
|
+
// Use local filtering to find matching instances
|
|
187
|
+
const requiredPaths = getRequiredFields(lookupQuery, ModelClass);
|
|
188
|
+
const prunedData = allInstances.map(inst => pickRequiredFields(requiredPaths, ModelClass.fromPk(inst[primaryKeyField], queryset)));
|
|
189
|
+
const matchingPks = processQuery(prunedData, lookupQuery, ModelClass);
|
|
190
|
+
// Find the corresponding instances
|
|
191
|
+
const matchingInstances = allInstances.filter(inst => matchingPks.includes(inst[primaryKeyField]));
|
|
192
|
+
const isCreatingNew = matchingInstances.length === 0;
|
|
193
|
+
const effectiveType = isCreatingNew ? Type.CREATE : Type.UPDATE;
|
|
194
|
+
// Create the instance data
|
|
195
|
+
const instanceData = isCreatingNew
|
|
196
|
+
? { ...lookup, ...defaults, [primaryKeyField]: opId }
|
|
197
|
+
: matchingInstances[0];
|
|
198
|
+
return new Operation({
|
|
199
|
+
operationId: opId,
|
|
200
|
+
type: effectiveType,
|
|
201
|
+
instances: [instanceData],
|
|
202
|
+
queryset: queryset,
|
|
203
|
+
args: { lookup, defaults },
|
|
204
|
+
localOnly: queryset._optimisticOnly || false,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create an UPDATE_OR_CREATE operation with local filtering logic
|
|
209
|
+
* @param {QuerySet} queryset - The queryset context
|
|
210
|
+
* @param {Object} lookup - The lookup criteria
|
|
211
|
+
* @param {Object} defaults - The default values for creation/update
|
|
212
|
+
* @param {string} [operationId] - Optional operation ID (for hotpath events)
|
|
213
|
+
* @returns {Operation} The created operation
|
|
214
|
+
*/
|
|
215
|
+
static createUpdateOrCreateOperation(queryset, lookup = {}, defaults = {}, operationId = null) {
|
|
216
|
+
const ModelClass = queryset.ModelClass;
|
|
217
|
+
const primaryKeyField = ModelClass.primaryKeyField;
|
|
218
|
+
const opId = operationId || `${uuid7()}`;
|
|
219
|
+
// Get all current instances from the store for local filtering
|
|
220
|
+
const modelStore = modelStoreRegistry.getStore(ModelClass);
|
|
221
|
+
const allInstances = modelStore.render();
|
|
222
|
+
// Create a queryset filter for the lookup criteria
|
|
223
|
+
const lookupFilter = { ...lookup };
|
|
224
|
+
const lookupQuerySet = new queryset.constructor(ModelClass).filter(lookupFilter);
|
|
225
|
+
const lookupQuery = lookupQuerySet.build();
|
|
226
|
+
// Use local filtering to find matching instances
|
|
227
|
+
const requiredPaths = getRequiredFields(lookupQuery, ModelClass);
|
|
228
|
+
const prunedData = allInstances.map(inst => pickRequiredFields(requiredPaths, ModelClass.fromPk(inst[primaryKeyField], queryset)));
|
|
229
|
+
const matchingPks = processQuery(prunedData, lookupQuery, ModelClass);
|
|
230
|
+
// Find the corresponding instances
|
|
231
|
+
const matchingInstances = allInstances.filter(inst => matchingPks.includes(inst[primaryKeyField]));
|
|
232
|
+
const isCreatingNew = matchingInstances.length === 0;
|
|
233
|
+
const isUpdating = !isCreatingNew;
|
|
234
|
+
const effectiveType = isCreatingNew ? Type.CREATE : Type.UPDATE;
|
|
235
|
+
// Create the instance data
|
|
236
|
+
const instanceData = isCreatingNew
|
|
237
|
+
? { ...lookup, ...defaults, [primaryKeyField]: opId }
|
|
238
|
+
: { ...matchingInstances[0], ...defaults };
|
|
239
|
+
return new Operation({
|
|
240
|
+
operationId: opId,
|
|
241
|
+
type: effectiveType,
|
|
242
|
+
instances: [instanceData],
|
|
243
|
+
queryset: queryset,
|
|
244
|
+
args: { lookup, defaults },
|
|
245
|
+
localOnly: queryset._optimisticOnly || false,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|