@statezero/core 0.1.60 → 0.1.62
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.
|
@@ -110,6 +110,19 @@ export async function makeApiCall(querySet, operationType, args = {}, operationI
|
|
|
110
110
|
throw new Error(`API call failed: ${error.message}`);
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
|
-
|
|
113
|
+
/*
|
|
114
|
+
We use p-queue for write operations to ensure that the user does not
|
|
115
|
+
try and perform a write operation using a temporary primary key that
|
|
116
|
+
exists on the local optimistic instance until the backend has replied with the
|
|
117
|
+
new primary key. This would result in a 400 Bad Request error from the backend.
|
|
118
|
+
This is effective, but does mean that write operations are queued which can lead to delays.
|
|
119
|
+
In general, this is not a problem because the UI will optimistically update the state so the
|
|
120
|
+
user does not perceive the delay, but it is something to be aware of.
|
|
121
|
+
|
|
122
|
+
In the future, we will want to implement an approach where we specifically detect
|
|
123
|
+
if a write operation is using a temporary pk and then await for the temporary
|
|
124
|
+
pk to be replaced before proceeding with the write operation. But for now,
|
|
125
|
+
this is a simple and effective solution.
|
|
126
|
+
*/
|
|
114
127
|
return isWriteOperation ? apiCallQueue.add(apiCall) : apiCall();
|
|
115
128
|
}
|
|
@@ -54,14 +54,7 @@ export class Manager {
|
|
|
54
54
|
* @returns {Promise<Model>} A promise that resolves to the model instance.
|
|
55
55
|
*/
|
|
56
56
|
async get(filters, serializerOptions) {
|
|
57
|
-
|
|
58
|
-
if (filters) {
|
|
59
|
-
querySet = querySet.filter(filters);
|
|
60
|
-
}
|
|
61
|
-
if (serializerOptions) {
|
|
62
|
-
querySet.setSerializerOptions(serializerOptions);
|
|
63
|
-
}
|
|
64
|
-
return await QueryExecutor.execute(querySet, 'get');
|
|
57
|
+
return this.newQuerySet().get(filters, serializerOptions);
|
|
65
58
|
}
|
|
66
59
|
/**
|
|
67
60
|
* Filters the QuerySet based on the provided conditions.
|
|
@@ -168,7 +161,7 @@ export class Manager {
|
|
|
168
161
|
* @returns {Promise<*>} A promise that resolves to the newly created model instance.
|
|
169
162
|
*/
|
|
170
163
|
async create(data) {
|
|
171
|
-
return
|
|
164
|
+
return this.newQuerySet().create(data);
|
|
172
165
|
}
|
|
173
166
|
/**
|
|
174
167
|
* Fetches all records using the current QuerySet.
|
|
@@ -177,11 +170,7 @@ export class Manager {
|
|
|
177
170
|
* @returns {Promise<Array<*>>} A promise that resolves to an array of model instances.
|
|
178
171
|
*/
|
|
179
172
|
async fetch(serializerOptions) {
|
|
180
|
-
|
|
181
|
-
if (serializerOptions) {
|
|
182
|
-
querySet.setSerializerOptions(serializerOptions);
|
|
183
|
-
}
|
|
184
|
-
return await QueryExecutor.execute(querySet, 'list');
|
|
173
|
+
return this.newQuerySet().fetch(serializerOptions);
|
|
185
174
|
}
|
|
186
175
|
/**
|
|
187
176
|
* Retrieves or creates a model instance based on lookup fields and defaults.
|
|
@@ -194,7 +183,7 @@ export class Manager {
|
|
|
194
183
|
*/
|
|
195
184
|
async getOrCreate(lookupFields, options = {}) {
|
|
196
185
|
const { defaults = {} } = options;
|
|
197
|
-
return
|
|
186
|
+
return this.newQuerySet().getOrCreate(lookupFields, defaults);
|
|
198
187
|
}
|
|
199
188
|
/**
|
|
200
189
|
* Updates or creates a model instance based on lookup fields and defaults.
|
|
@@ -207,7 +196,7 @@ export class Manager {
|
|
|
207
196
|
*/
|
|
208
197
|
async updateOrCreate(lookupFields, options = {}) {
|
|
209
198
|
const { defaults = {} } = options;
|
|
210
|
-
return
|
|
199
|
+
return this.newQuerySet().updateOrCreate(lookupFields, defaults);
|
|
211
200
|
}
|
|
212
201
|
/**
|
|
213
202
|
* Applies a search to the QuerySet using the specified search query and fields.
|
|
@@ -35,6 +35,38 @@ function relatedQuerysets(queryset) {
|
|
|
35
35
|
});
|
|
36
36
|
return result;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns querysets that contain any of the specified instances
|
|
40
|
+
* @param {Operation} operation - The operation containing instances to check
|
|
41
|
+
* @returns {Map<QuerySet, Store>}
|
|
42
|
+
*/
|
|
43
|
+
function querysetsContainingInstances(operation) {
|
|
44
|
+
const ModelClass = operation.queryset.ModelClass;
|
|
45
|
+
const pkField = ModelClass.primaryKeyField;
|
|
46
|
+
const instancePks = new Set(operation.instances
|
|
47
|
+
.filter(instance => instance && typeof instance === 'object' && pkField in instance)
|
|
48
|
+
.map(instance => instance[pkField]));
|
|
49
|
+
if (instancePks.size === 0) {
|
|
50
|
+
return new Map();
|
|
51
|
+
}
|
|
52
|
+
const result = new Map();
|
|
53
|
+
Array.from(querysetStoreRegistry._stores.entries()).forEach(([queryset, store]) => {
|
|
54
|
+
if (store.modelClass !== ModelClass)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
// Check if this queryset contains any of the instances
|
|
58
|
+
const renderedPks = new Set(store.render(false)); // Get without optimistic updates
|
|
59
|
+
const hasIntersection = [...instancePks].some(pk => renderedPks.has(pk));
|
|
60
|
+
if (hasIntersection) {
|
|
61
|
+
result.set(store.queryset, store);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
console.warn('Error checking queryset for instances', e);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
38
70
|
/**
|
|
39
71
|
* Process an operation in the model store
|
|
40
72
|
*
|
|
@@ -124,9 +156,14 @@ function processQuerysetStores(operation, actionType) {
|
|
|
124
156
|
function processMetricStores(operation, actionType) {
|
|
125
157
|
const queryset = operation.queryset;
|
|
126
158
|
const ModelClass = queryset.ModelClass;
|
|
127
|
-
// For metrics, we
|
|
128
|
-
// and always use related querysets since metrics are aggregations
|
|
159
|
+
// For metrics, we use related querysets as the base
|
|
129
160
|
const allQuerysets = Array.from(relatedQuerysets(queryset).keys());
|
|
161
|
+
// For update/delete operations, also include querysets that contain the instances
|
|
162
|
+
if (operation.type === Type.UPDATE || operation.type === Type.UPDATE_INSTANCE ||
|
|
163
|
+
operation.type === Type.DELETE || operation.type === Type.DELETE_INSTANCE) {
|
|
164
|
+
const containingQuerysets = Array.from(querysetsContainingInstances(operation).keys());
|
|
165
|
+
allQuerysets.push(...containingQuerysets);
|
|
166
|
+
}
|
|
130
167
|
let allMetricStores = new Set();
|
|
131
168
|
allQuerysets.forEach(qs => {
|
|
132
169
|
const stores = metricRegistry.getAllStoresForQueryset(qs);
|
package/package.json
CHANGED