remult 3.3.19-next.2 → 3.4.0-next.2
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/SseSubscriptionServer.d.ts +2 -0
- package/SseSubscriptionServer.js +4 -2
- package/esm/SseSubscriptionServer.js +3 -1
- package/esm/index.js +6 -7
- package/esm/live-query/data-provider-live-query-storage.js +3 -1
- package/esm/remult-d1.js +0 -1
- package/esm/remult-knex/index.js +2 -112
- package/esm/remult-mongo.js +8 -17
- package/esm/remult-sqlite-core.js +0 -1
- package/esm/server/remult-api-server.js +6 -1
- package/esm/src/data-api.js +1 -0
- package/esm/src/data-interfaces.js +4 -4
- package/esm/src/data-providers/array-entity-data-provider.js +2 -14
- package/esm/src/data-providers/json-data-provider.js +2 -2
- package/esm/src/data-providers/rest-data-provider.js +12 -11
- package/esm/src/data-providers/sql-database.js +43 -110
- package/esm/src/live-query/LiveQueryClient.js +163 -52
- package/esm/src/live-query/SseSubscriptionClient.js +101 -43
- package/esm/src/live-query/SubscriptionChannel.js +37 -3
- package/esm/src/live-query/SubscriptionServer.js +11 -3
- package/esm/src/remult3/RepositoryImplementation.js +60 -90
- package/esm/src/remult3/remult3.js +6 -0
- package/index.d.ts +17 -31
- package/index.js +2 -5
- package/live-query/data-provider-live-query-storage.d.ts +4 -1
- package/live-query/data-provider-live-query-storage.js +18 -15
- package/package.json +1 -1
- package/remult-d1.d.ts +0 -1
- package/remult-d1.js +0 -1
- package/remult-knex/index.js +44 -329
- package/remult-mongo.js +36 -80
- package/remult-sqlite-core.d.ts +0 -1
- package/remult-sqlite-core.js +0 -1
- package/server/remult-api-server.js +13 -9
- package/src/data-api.js +1 -0
- package/src/data-interfaces.d.ts +3 -2
- package/src/data-interfaces.js +4 -4
- package/src/data-providers/array-entity-data-provider.d.ts +2 -2
- package/src/data-providers/array-entity-data-provider.js +14 -92
- package/src/data-providers/json-data-provider.js +2 -2
- package/src/data-providers/rest-data-provider.d.ts +3 -2
- package/src/data-providers/rest-data-provider.js +17 -18
- package/src/data-providers/sql-database.js +105 -254
- package/src/live-query/LiveQueryClient.js +215 -97
- package/src/live-query/SseSubscriptionClient.js +143 -83
- package/src/live-query/SubscriptionChannel.d.ts +8 -0
- package/src/live-query/SubscriptionChannel.js +47 -9
- package/src/live-query/SubscriptionServer.d.ts +7 -2
- package/src/live-query/SubscriptionServer.js +20 -10
- package/src/remult3/RepositoryImplementation.d.ts +1 -1
- package/src/remult3/RepositoryImplementation.js +270 -384
- package/src/remult3/remult3.d.ts +8 -2
- package/src/remult3/remult3.js +6 -0
- package/src/sql-command.d.ts +0 -2
- package/src/standard-schema/index.d.ts +1 -1
- package/esm/src/data-providers/indexed-db-data-provider.js +0 -811
- package/src/data-providers/indexed-db-data-provider.d.ts +0 -39
- package/src/data-providers/indexed-db-data-provider.js +0 -1387
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { GenericResponse } from './server/index.js';
|
|
2
2
|
import type { Remult } from './src/context.js';
|
|
3
3
|
import type { SubscriptionServer } from './src/live-query/SubscriptionServer.js';
|
|
4
|
+
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
5
|
+
export declare const ssePingMs = 15000;
|
|
4
6
|
export declare class SseSubscriptionServer implements SubscriptionServer {
|
|
5
7
|
private canUserConnectToChannel?;
|
|
6
8
|
constructor(canUserConnectToChannel?: ((channel: string, remult: Remult) => boolean) | undefined);
|
package/SseSubscriptionServer.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.clientConnection = exports.SseSubscriptionServer = void 0;
|
|
3
|
+
exports.clientConnection = exports.SseSubscriptionServer = exports.ssePingMs = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var SseSubscriptionClient_js_1 = require("./src/live-query/SseSubscriptionClient.js");
|
|
6
|
+
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
7
|
+
exports.ssePingMs = 15000;
|
|
6
8
|
var SseSubscriptionServer = /** @class */ (function () {
|
|
7
9
|
function SseSubscriptionServer(canUserConnectToChannel) {
|
|
8
10
|
this.canUserConnectToChannel = canUserConnectToChannel;
|
|
@@ -137,7 +139,7 @@ var clientConnection = /** @class */ (function () {
|
|
|
137
139
|
this.write('', 'keep-alive');
|
|
138
140
|
this.timeOutRef = setTimeout(function () {
|
|
139
141
|
_this.sendLiveMessage();
|
|
140
|
-
},
|
|
142
|
+
}, exports.ssePingMs);
|
|
141
143
|
};
|
|
142
144
|
return clientConnection;
|
|
143
145
|
}());
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ConnectionNotFoundError } from './src/live-query/SseSubscriptionClient.js';
|
|
2
|
+
/** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
|
|
3
|
+
export const ssePingMs = 15_000;
|
|
2
4
|
export class SseSubscriptionServer {
|
|
3
5
|
canUserConnectToChannel;
|
|
4
6
|
//@internal
|
|
@@ -101,6 +103,6 @@ export class clientConnection {
|
|
|
101
103
|
this.write('', 'keep-alive');
|
|
102
104
|
this.timeOutRef = setTimeout(() => {
|
|
103
105
|
this.sendLiveMessage();
|
|
104
|
-
},
|
|
106
|
+
}, ssePingMs);
|
|
105
107
|
}
|
|
106
108
|
}
|
package/esm/index.js
CHANGED
|
@@ -9,32 +9,31 @@ export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
|
|
|
9
9
|
export const CaptionTransformer = LabelTransformer;
|
|
10
10
|
export { Entity } from './src/remult3/entity.js';
|
|
11
11
|
export { getEntityRef } from './src/remult3/getEntityRef.js';
|
|
12
|
-
export { Field, Fields, Relations } from './src/remult3/Fields.js';
|
|
12
|
+
export { Field, Fields, Relations, } from './src/remult3/Fields.js';
|
|
13
13
|
export { IdEntity } from './src/remult3/IdEntity.js';
|
|
14
14
|
export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
|
|
15
|
-
export { EntityError } from './src/data-interfaces.js'; //V
|
|
15
|
+
export { EntityError, } from './src/data-interfaces.js'; //V
|
|
16
16
|
export { RestDataProvider } from './src/data-providers/rest-data-provider.js'; //V
|
|
17
17
|
export { InMemoryDataProvider } from './src/data-providers/in-memory-database.js'; //V
|
|
18
18
|
export { SqlDatabase } from './src/data-providers/sql-database.js'; //V
|
|
19
19
|
export { CustomSqlFilterBuilder, dbNamesOf, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
20
|
-
export { JsonDataProvider } from './src/data-providers/json-data-provider.js'; //V
|
|
20
|
+
export { JsonDataProvider, } from './src/data-providers/json-data-provider.js'; //V
|
|
21
21
|
export { JsonEntityOpfsStorage } from './src/data-providers/json-entity-opfs-storage.js';
|
|
22
22
|
export { JsonEntityIndexedDbStorage } from './src/data-providers/json-entity-indexed-db-data-provider.js';
|
|
23
|
-
export { IndexedDbDataProvider, IndexedDbIndexBuilder, } from './src/data-providers/indexed-db-data-provider.js';
|
|
24
23
|
//export * from './src/data-api'; //reconsider if to make internal
|
|
25
24
|
export { Controller, BackendMethod, ProgressListener, ForbiddenError, } from './src/server-action.js';
|
|
26
25
|
export { Allow, Remult, withRemult, isBackend, EventSource, } from './src/context.js';
|
|
27
26
|
export { Sort } from './src/sort.js';
|
|
28
27
|
export { CompoundIdField } from './src/CompoundIdField.js';
|
|
29
|
-
export { Filter } from './src/filter/filter-interfaces.js';
|
|
28
|
+
export { Filter, } from './src/filter/filter-interfaces.js';
|
|
30
29
|
export { UrlBuilder } from './urlBuilder.js';
|
|
31
30
|
export { Validators, valueValidator, createValidator, createValidatorWithArgs, createValueValidatorWithArgs, createValueValidator, } from './src/validators.js';
|
|
32
31
|
export { ValueConverters } from './src/valueConverters.js';
|
|
33
32
|
export { remult } from './src/remult-proxy.js';
|
|
34
33
|
import { remult } from './src/remult-proxy.js';
|
|
35
34
|
//export { getId } from './src/remult3/getId';
|
|
36
|
-
export { InMemoryLiveQueryStorage } from './src/live-query/SubscriptionServer.js';
|
|
37
|
-
export { SubscriptionChannel } from './src/live-query/SubscriptionChannel.js';
|
|
35
|
+
export { InMemoryLiveQueryStorage, } from './src/live-query/SubscriptionServer.js';
|
|
36
|
+
export { SubscriptionChannel, } from './src/live-query/SubscriptionChannel.js';
|
|
38
37
|
/**
|
|
39
38
|
* A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
|
|
40
39
|
*
|
|
@@ -52,12 +52,14 @@ export class DataProviderLiveQueryStorage {
|
|
|
52
52
|
}
|
|
53
53
|
async keepAliveAndReturnUnknownQueryIds(queryIds) {
|
|
54
54
|
const repo = await this.repo;
|
|
55
|
+
const versions = {};
|
|
55
56
|
for (const query of await repo.find({ where: { id: queryIds } })) {
|
|
56
57
|
query.lastUsedIso = new Date().toISOString();
|
|
57
58
|
await repo.save(query);
|
|
59
|
+
versions[query.id] = query.data?.version ?? 0;
|
|
58
60
|
queryIds = queryIds.filter((x) => x !== query.id);
|
|
59
61
|
}
|
|
60
|
-
return queryIds;
|
|
62
|
+
return { unknownQueryIds: queryIds, versions };
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
let LiveQueryStorageEntity = class LiveQueryStorageEntity extends EntityBase {
|
package/esm/remult-d1.js
CHANGED
package/esm/remult-knex/index.js
CHANGED
|
@@ -234,87 +234,16 @@ class KnexEntityDataProvider {
|
|
|
234
234
|
return undefined;
|
|
235
235
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
236
236
|
}
|
|
237
|
-
async delete(
|
|
238
|
-
if (ids.length === 0)
|
|
239
|
-
return;
|
|
237
|
+
async delete(id) {
|
|
240
238
|
const e = await this.init();
|
|
241
239
|
let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
|
|
242
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
240
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
243
241
|
let where = await f.resolveWhere();
|
|
244
242
|
await this.getEntityFrom(e)
|
|
245
243
|
.delete()
|
|
246
244
|
.where((b) => where.forEach((w) => w(b)));
|
|
247
245
|
}
|
|
248
246
|
async insert(data, options) {
|
|
249
|
-
if (data.length === 0)
|
|
250
|
-
return [];
|
|
251
|
-
if (isMysqlKnex(this.knex) &&
|
|
252
|
-
isAutoIncrement(this.entity.idMetadata.field) &&
|
|
253
|
-
options?.select !== 'none') {
|
|
254
|
-
const result = [];
|
|
255
|
-
for (const row of data)
|
|
256
|
-
result.push(await this.insertOne(row, options));
|
|
257
|
-
return result;
|
|
258
|
-
}
|
|
259
|
-
const e = await this.init();
|
|
260
|
-
const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row), knexMaxParametersInOneSqlStatement(this.knex));
|
|
261
|
-
const result = [];
|
|
262
|
-
for (const batch of batches)
|
|
263
|
-
result.push(...(await this.insertBatch(batch, e, options)));
|
|
264
|
-
return result;
|
|
265
|
-
}
|
|
266
|
-
async insertBatch(batch, e, options) {
|
|
267
|
-
const cols = [];
|
|
268
|
-
for (const x of this.entity.fields) {
|
|
269
|
-
if (isDbReadonly(x, e))
|
|
270
|
-
continue;
|
|
271
|
-
if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key]) !=
|
|
272
|
-
undefined))
|
|
273
|
-
cols.push(x);
|
|
274
|
-
}
|
|
275
|
-
const insertObjects = [];
|
|
276
|
-
for (const row of batch) {
|
|
277
|
-
const insertObject = {};
|
|
278
|
-
for (const x of cols) {
|
|
279
|
-
const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key]);
|
|
280
|
-
const key = await e.$dbNameOf(x);
|
|
281
|
-
if (v != undefined)
|
|
282
|
-
insertObject[key] = v;
|
|
283
|
-
else if (x.allowNull)
|
|
284
|
-
insertObject[key] = null;
|
|
285
|
-
else
|
|
286
|
-
insertObject[key] = this.knex.raw('DEFAULT');
|
|
287
|
-
}
|
|
288
|
-
insertObjects.push(insertObject);
|
|
289
|
-
}
|
|
290
|
-
let insert = this.getEntityFrom(e).insert(insertObjects);
|
|
291
|
-
const wantRows = options?.select !== 'none';
|
|
292
|
-
if (isAutoIncrement(this.entity.idMetadata.field) && wantRows) {
|
|
293
|
-
const result = await insert.returning(this.entity.idMetadata.field.dbName);
|
|
294
|
-
const ids = result.map((r) => r && typeof r === 'object'
|
|
295
|
-
? r[this.entity.idMetadata.field.dbName]
|
|
296
|
-
: r);
|
|
297
|
-
return this.loadRowsByIds(ids);
|
|
298
|
-
}
|
|
299
|
-
await insert;
|
|
300
|
-
if (!wantRows)
|
|
301
|
-
return batch.map(() => undefined);
|
|
302
|
-
return this.loadRowsByIds(batch.map((row) => this.entity.idMetadata.getId(row)));
|
|
303
|
-
}
|
|
304
|
-
async loadRowsByIds(ids) {
|
|
305
|
-
const found = await this.find({
|
|
306
|
-
where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
|
|
307
|
-
});
|
|
308
|
-
const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
|
|
309
|
-
return ids.map((id) => {
|
|
310
|
-
const row = byId.get(id + '');
|
|
311
|
-
if (!row)
|
|
312
|
-
throw new Error(`Failed to insert row - result contained ${found.length} rows`);
|
|
313
|
-
return row;
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
//@internal
|
|
317
|
-
async insertOne(data, options) {
|
|
318
247
|
const e = await this.init();
|
|
319
248
|
let insertObject = {};
|
|
320
249
|
for (const x of this.entity.fields) {
|
|
@@ -662,45 +591,6 @@ export async function createKnexDataProvider(config) {
|
|
|
662
591
|
let result = new KnexDataProvider(k);
|
|
663
592
|
return result;
|
|
664
593
|
}
|
|
665
|
-
const defaultMaxParametersInOneSqlStatement = 2000;
|
|
666
|
-
function isMysqlKnex(knex) {
|
|
667
|
-
const client = knex.client.config.client;
|
|
668
|
-
return client === 'mysql2' || client === 'mysql';
|
|
669
|
-
}
|
|
670
|
-
function knexMaxParametersInOneSqlStatement(knex) {
|
|
671
|
-
const client = String(knex.client.config.client ?? '');
|
|
672
|
-
if (client.includes('sqlite'))
|
|
673
|
-
return 999;
|
|
674
|
-
return defaultMaxParametersInOneSqlStatement;
|
|
675
|
-
}
|
|
676
|
-
function splitByMaxParameters(items, parametersInItem, maxParameters) {
|
|
677
|
-
const batches = [];
|
|
678
|
-
let batch = [];
|
|
679
|
-
let params = 0;
|
|
680
|
-
for (const item of items) {
|
|
681
|
-
const n = parametersInItem(item);
|
|
682
|
-
if (batch.length > 0 && params + n > maxParameters) {
|
|
683
|
-
batches.push(batch);
|
|
684
|
-
batch = [];
|
|
685
|
-
params = 0;
|
|
686
|
-
}
|
|
687
|
-
batch.push(item);
|
|
688
|
-
params += n;
|
|
689
|
-
}
|
|
690
|
-
if (batch.length)
|
|
691
|
-
batches.push(batch);
|
|
692
|
-
return batches;
|
|
693
|
-
}
|
|
694
|
-
function countKnexInsertBindParameters(entity, e, row) {
|
|
695
|
-
let n = 0;
|
|
696
|
-
for (const x of entity.fields) {
|
|
697
|
-
if (isDbReadonly(x, e))
|
|
698
|
-
continue;
|
|
699
|
-
if (translateValueAndHandleArrayAndHandleArray(x, row[x.key]) != undefined)
|
|
700
|
-
n++;
|
|
701
|
-
}
|
|
702
|
-
return n;
|
|
703
|
-
}
|
|
704
594
|
function translateValueAndHandleArrayAndHandleArray(field, val) {
|
|
705
595
|
let result = field.valueConverter.toDb(val);
|
|
706
596
|
if (Array.isArray(result))
|
package/esm/remult-mongo.js
CHANGED
|
@@ -266,31 +266,22 @@ class MongoEntityDataProvider {
|
|
|
266
266
|
return undefined;
|
|
267
267
|
return getRowAfterUpdate(this.entity, this, data, id, 'update');
|
|
268
268
|
}
|
|
269
|
-
async delete(
|
|
270
|
-
if (ids.length === 0)
|
|
271
|
-
return;
|
|
269
|
+
async delete(id) {
|
|
272
270
|
const { e, collection } = await this.collection();
|
|
273
271
|
let f = new FilterConsumerBridgeToMongo(e);
|
|
274
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
275
|
-
await collection.
|
|
272
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
273
|
+
await collection.deleteOne(await f.resolveWhere(), {
|
|
276
274
|
session: this.session,
|
|
277
275
|
});
|
|
278
276
|
}
|
|
279
277
|
async insert(data, options) {
|
|
280
|
-
if (data.length === 0)
|
|
281
|
-
return [];
|
|
282
278
|
let { collection, e } = await this.collection();
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const r = await collection.insertMany(docs, { session: this.session });
|
|
279
|
+
let r = await collection.insertOne(await this.translateToDb(data, e), {
|
|
280
|
+
session: this.session,
|
|
281
|
+
});
|
|
287
282
|
if (options?.select === 'none')
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
for (let i = 0; i < data.length; i++) {
|
|
291
|
-
result.push(await this.translateFromDb(await collection.findOne({ _id: r.insertedIds[i] }, { session: this.session }), e));
|
|
292
|
-
}
|
|
293
|
-
return result;
|
|
283
|
+
return undefined;
|
|
284
|
+
return await this.translateFromDb(await collection.findOne({ _id: r.insertedId }, { session: this.session }), e);
|
|
294
285
|
}
|
|
295
286
|
async collection() {
|
|
296
287
|
const e = await dbNamesOf(this.entity);
|
|
@@ -13,7 +13,6 @@ export class SqliteCoreDataProvider {
|
|
|
13
13
|
this.doesNotSupportReturningSyntaxOnlyForUpdate = doesNotSupportReturningSyntaxOnlyForUpdate;
|
|
14
14
|
}
|
|
15
15
|
orderByNullsFirst;
|
|
16
|
-
maxParametersInOneSqlStatement = 999;
|
|
17
16
|
getLimitSqlSyntax(limit, offset) {
|
|
18
17
|
return ' limit ' + limit + ' offset ' + offset;
|
|
19
18
|
}
|
|
@@ -281,7 +281,12 @@ export class RemultServerImplementation {
|
|
|
281
281
|
}));
|
|
282
282
|
}
|
|
283
283
|
r.route(this.options.rootPath + '/' + liveQueryKeepAliveRoute).post(this.process(async (remult, req, res, reqInfo, origRes, origReq) => {
|
|
284
|
-
|
|
284
|
+
const body = await this.coreOptions.getRequestBody(origReq);
|
|
285
|
+
const queryIds = Array.isArray(body)
|
|
286
|
+
? body
|
|
287
|
+
: (body?.queryIds ?? []);
|
|
288
|
+
const result = await remult.liveQueryStorage.keepAliveAndReturnUnknownQueryIds(queryIds);
|
|
289
|
+
res.success(Array.isArray(body) ? result.unknownQueryIds : result);
|
|
285
290
|
}));
|
|
286
291
|
}
|
|
287
292
|
this.entities.forEach((e) => {
|
package/esm/src/data-api.js
CHANGED
|
@@ -358,6 +358,7 @@ export class DataApi {
|
|
|
358
358
|
requestJson: await serializeContext(),
|
|
359
359
|
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
360
360
|
lastIds: r.r.map((y) => this.repository.metadata.idMetadata.getId(y)),
|
|
361
|
+
version: 0,
|
|
361
362
|
};
|
|
362
363
|
await this.remult.liveQueryStorage.add({
|
|
363
364
|
entityKey: this.repository.metadata.key,
|
|
@@ -40,11 +40,11 @@ class EntityDataProviderPromiseWrapper {
|
|
|
40
40
|
constructor(dataProvider) {
|
|
41
41
|
this.dataProvider = dataProvider;
|
|
42
42
|
}
|
|
43
|
-
delete(
|
|
44
|
-
return this.dataProvider.then((dp) => dp.delete(
|
|
43
|
+
delete(id) {
|
|
44
|
+
return this.dataProvider.then((dp) => dp.delete(id));
|
|
45
45
|
}
|
|
46
|
-
insert(data
|
|
47
|
-
return this.dataProvider.then((dp) => dp.insert(data
|
|
46
|
+
insert(data) {
|
|
47
|
+
return this.dataProvider.then((dp) => dp.insert(data));
|
|
48
48
|
}
|
|
49
49
|
count(where) {
|
|
50
50
|
return this.dataProvider.then((dp) => dp.count(where));
|
|
@@ -318,30 +318,18 @@ export class ArrayEntityDataProvider {
|
|
|
318
318
|
}
|
|
319
319
|
throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to update`);
|
|
320
320
|
}
|
|
321
|
-
async delete(
|
|
322
|
-
for (const id of ids)
|
|
323
|
-
await this.deleteOne(id);
|
|
324
|
-
}
|
|
325
|
-
//@internal
|
|
326
|
-
async deleteOne(id) {
|
|
321
|
+
async delete(id) {
|
|
327
322
|
const names = await this.init();
|
|
328
323
|
let idMatches = this.idMatches(id, names);
|
|
329
324
|
for (let i = 0; i < this.rows().length; i++) {
|
|
330
325
|
if (idMatches(this.rows()[i])) {
|
|
331
326
|
this.rows().splice(i, 1);
|
|
332
|
-
return;
|
|
327
|
+
return Promise.resolve();
|
|
333
328
|
}
|
|
334
329
|
}
|
|
335
330
|
throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to delete`);
|
|
336
331
|
}
|
|
337
332
|
async insert(data) {
|
|
338
|
-
const result = [];
|
|
339
|
-
for (const row of data)
|
|
340
|
-
result.push(await this.insertOne(row));
|
|
341
|
-
return result;
|
|
342
|
-
}
|
|
343
|
-
//@internal
|
|
344
|
-
async insertOne(data) {
|
|
345
333
|
const names = await this.init();
|
|
346
334
|
let j = this.translateToJson(data, names);
|
|
347
335
|
let idf = this.entity.idMetadata.field;
|
|
@@ -49,8 +49,8 @@ class JsonEntityDataProvider {
|
|
|
49
49
|
return x;
|
|
50
50
|
}))));
|
|
51
51
|
}
|
|
52
|
-
delete(
|
|
53
|
-
return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.delete(
|
|
52
|
+
delete(id) {
|
|
53
|
+
return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.delete(id).then(async (x) => {
|
|
54
54
|
await save();
|
|
55
55
|
return x;
|
|
56
56
|
}))));
|
|
@@ -303,20 +303,21 @@ export class RestEntityDataProvider {
|
|
|
303
303
|
}
|
|
304
304
|
return result;
|
|
305
305
|
}
|
|
306
|
-
async delete(
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return this.http().delete(this.url() + '/' + encodeURIComponent(
|
|
311
|
-
await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)));
|
|
306
|
+
async delete(id) {
|
|
307
|
+
if (id == '')
|
|
308
|
+
await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)));
|
|
309
|
+
else
|
|
310
|
+
return this.http().delete(this.url() + '/' + encodeURIComponent(id));
|
|
312
311
|
}
|
|
313
312
|
insert(data, options) {
|
|
314
313
|
return this.http()
|
|
315
|
-
.post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''),
|
|
316
|
-
.then((y) =>
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
314
|
+
.post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), this.translateToJson(data))
|
|
315
|
+
.then((y) => this.translateFromJson(y));
|
|
316
|
+
}
|
|
317
|
+
insertMany(data, options) {
|
|
318
|
+
return this.http()
|
|
319
|
+
.post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), data.map((data) => this.translateToJson(data)))
|
|
320
|
+
.then((y) => y.map((y) => this.translateFromJson(y)));
|
|
320
321
|
}
|
|
321
322
|
}
|
|
322
323
|
export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
|
|
@@ -117,8 +117,6 @@ export class SqlDatabase {
|
|
|
117
117
|
doesNotSupportReturningSyntax: this.sql.doesNotSupportReturningSyntax,
|
|
118
118
|
doesNotSupportReturningSyntaxOnlyForUpdate: this.sql.doesNotSupportReturningSyntaxOnlyForUpdate,
|
|
119
119
|
orderByNullsFirst: this.sql.orderByNullsFirst,
|
|
120
|
-
afterMutation: this.sql.afterMutation,
|
|
121
|
-
maxParametersInOneSqlStatement: this.sql.maxParametersInOneSqlStatement,
|
|
122
120
|
}));
|
|
123
121
|
}
|
|
124
122
|
finally {
|
|
@@ -195,35 +193,6 @@ export class SqlDatabase {
|
|
|
195
193
|
createdEntities = [];
|
|
196
194
|
end;
|
|
197
195
|
}
|
|
198
|
-
const defaultMaxParametersInOneSqlStatement = 2000;
|
|
199
|
-
function splitByMaxParameters(items, parametersInItem, maxParameters) {
|
|
200
|
-
const batches = [];
|
|
201
|
-
let batch = [];
|
|
202
|
-
let params = 0;
|
|
203
|
-
for (const item of items) {
|
|
204
|
-
const n = parametersInItem(item);
|
|
205
|
-
if (batch.length > 0 && params + n > maxParameters) {
|
|
206
|
-
batches.push(batch);
|
|
207
|
-
batch = [];
|
|
208
|
-
params = 0;
|
|
209
|
-
}
|
|
210
|
-
batch.push(item);
|
|
211
|
-
params += n;
|
|
212
|
-
}
|
|
213
|
-
if (batch.length)
|
|
214
|
-
batches.push(batch);
|
|
215
|
-
return batches;
|
|
216
|
-
}
|
|
217
|
-
function countInsertBindParameters(entity, e, row) {
|
|
218
|
-
let n = 0;
|
|
219
|
-
for (const x of entity.fields) {
|
|
220
|
-
if (isDbReadonly(x, e))
|
|
221
|
-
continue;
|
|
222
|
-
if (x.valueConverter.toDb(row[x.key]) != undefined)
|
|
223
|
-
n++;
|
|
224
|
-
}
|
|
225
|
-
return n;
|
|
226
|
-
}
|
|
227
196
|
const icons = new Map([
|
|
228
197
|
// CRUD
|
|
229
198
|
['INSERT', '⚪'], // Used to insert new data into a database.
|
|
@@ -472,13 +441,11 @@ class ActualSQLEntityDataProvider {
|
|
|
472
441
|
return this.buildResultRow(colKeys, sqlResult.rows[0], sqlResult);
|
|
473
442
|
});
|
|
474
443
|
}
|
|
475
|
-
async delete(
|
|
476
|
-
if (ids.length === 0)
|
|
477
|
-
return;
|
|
444
|
+
async delete(id) {
|
|
478
445
|
let e = await this.init();
|
|
479
446
|
let r = this.sql.createCommand();
|
|
480
447
|
let f = new FilterConsumerBridgeToSqlRequest(r, e);
|
|
481
|
-
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(
|
|
448
|
+
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
482
449
|
let statement = 'delete from ' + e.$entityName;
|
|
483
450
|
statement += await f.resolveWhere();
|
|
484
451
|
return r.execute(statement).then(() => {
|
|
@@ -486,90 +453,56 @@ class ActualSQLEntityDataProvider {
|
|
|
486
453
|
});
|
|
487
454
|
}
|
|
488
455
|
async insert(data, options) {
|
|
489
|
-
if (data.length === 0)
|
|
490
|
-
return [];
|
|
491
456
|
let e = await this.init();
|
|
492
|
-
const maxParams = this.strategy.maxParametersInOneSqlStatement ??
|
|
493
|
-
defaultMaxParametersInOneSqlStatement;
|
|
494
|
-
const batches = splitByMaxParameters(data, (row) => countInsertBindParameters(this.entity, e, row), maxParams);
|
|
495
|
-
const result = [];
|
|
496
|
-
for (const batch of batches)
|
|
497
|
-
result.push(...(await this.insertBatch(batch, e, options)));
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
//@internal
|
|
501
|
-
async insertOne(data, options) {
|
|
502
|
-
return (await this.insert([data], options))[0];
|
|
503
|
-
}
|
|
504
|
-
async insertBatch(batch, e, options) {
|
|
505
457
|
let r = this.sql.createCommand();
|
|
506
|
-
|
|
507
|
-
for (const x of this.entity.fields) {
|
|
508
|
-
if (isDbReadonly(x, e))
|
|
509
|
-
continue;
|
|
510
|
-
if (batch.some((row) => x.valueConverter.toDb(row[x.key]) != undefined))
|
|
511
|
-
cols.push(x);
|
|
512
|
-
}
|
|
513
|
-
let colSql = '';
|
|
458
|
+
let cols = '';
|
|
514
459
|
let vals = '';
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (v != undefined)
|
|
460
|
+
let added = false;
|
|
461
|
+
for (const x of this.entity.fields) {
|
|
462
|
+
if (isDbReadonly(x, e)) {
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
let v = x.valueConverter.toDb(data[x.key]);
|
|
466
|
+
if (v != undefined) {
|
|
467
|
+
if (!added)
|
|
468
|
+
added = true;
|
|
469
|
+
else {
|
|
470
|
+
cols += ', ';
|
|
471
|
+
vals += ', ';
|
|
472
|
+
}
|
|
473
|
+
cols += e.$dbNameOf(x);
|
|
530
474
|
vals += toDbSql(r, x, v);
|
|
531
|
-
|
|
532
|
-
vals += 'null';
|
|
533
|
-
else
|
|
534
|
-
vals += 'DEFAULT';
|
|
475
|
+
}
|
|
535
476
|
}
|
|
536
|
-
vals += ')';
|
|
537
477
|
}
|
|
538
|
-
let statement = `insert into ${e.$entityName} (${
|
|
478
|
+
let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
|
|
539
479
|
let { colKeys, select } = await this.buildSelect(e, r, undefined, undefined);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (!source.doesNotSupportReturningSyntax && wantRows)
|
|
480
|
+
if (!this.sql._getSourceSql().doesNotSupportReturningSyntax &&
|
|
481
|
+
!(options?.select === 'none'))
|
|
543
482
|
statement += ' returning ' + select;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
483
|
+
return await r.execute(statement).then((sql) => {
|
|
484
|
+
this.sql._getSourceSql().afterMutation?.();
|
|
485
|
+
if (this.sql._getSourceSql().doesNotSupportReturningSyntax) {
|
|
486
|
+
if (isAutoIncrement(this.entity.idMetadata.field)) {
|
|
487
|
+
const id = sql.rows[0];
|
|
488
|
+
if (typeof id !== 'number')
|
|
489
|
+
throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
|
|
490
|
+
JSON.stringify(id));
|
|
491
|
+
if (options?.select === 'none')
|
|
492
|
+
return undefined;
|
|
493
|
+
return this.find({
|
|
494
|
+
where: new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, id)),
|
|
495
|
+
}).then((r) => r[0]);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
if (options?.select === 'none')
|
|
499
|
+
return undefined;
|
|
500
|
+
return getRowAfterUpdate(this.entity, this, data, undefined, 'insert');
|
|
501
|
+
}
|
|
558
502
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
async loadRowsByIds(ids) {
|
|
564
|
-
const found = await this.find({
|
|
565
|
-
where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
|
|
566
|
-
});
|
|
567
|
-
const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
|
|
568
|
-
return ids.map((id) => {
|
|
569
|
-
const row = byId.get(id + '');
|
|
570
|
-
if (!row)
|
|
571
|
-
throw new Error(`Failed to insert row - result contained ${found.length} rows`);
|
|
572
|
-
return row;
|
|
503
|
+
if (options?.select === 'none')
|
|
504
|
+
return undefined;
|
|
505
|
+
return this.buildResultRow(colKeys, sql.rows[0], sql);
|
|
573
506
|
});
|
|
574
507
|
}
|
|
575
508
|
}
|