remult 0.25.7-exp.1 → 0.25.7-exp.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.
@@ -30,6 +30,10 @@ export class DataApi {
30
30
  return this.getArray(res, req, body);
31
31
  case 'count':
32
32
  return this.count(res, req, body);
33
+ case 'deleteMany':
34
+ return this.deleteMany(res, req, body);
35
+ case 'updateMany':
36
+ return this.updateMany(res, req, body);
33
37
  case 'endLiveQuery':
34
38
  await this.remult.liveQueryStorage.remove(body.id);
35
39
  res.success('ok');
@@ -60,6 +64,24 @@ export class DataApi {
60
64
  response.error(err);
61
65
  }
62
66
  }
67
+ async deleteMany(response, request, filterBody) {
68
+ try {
69
+ return await doTransaction(this.remult, async () => {
70
+ let deleted = 0;
71
+ for await (const x of this.repository.query({
72
+ where: await this.buildWhere(request, filterBody),
73
+ include: this.includeNone(),
74
+ })) {
75
+ await this.actualDelete(x);
76
+ deleted++;
77
+ }
78
+ response.success({ deleted });
79
+ });
80
+ }
81
+ catch (err) {
82
+ response.error(err);
83
+ }
84
+ }
63
85
  async getArrayImpl(response, request, filterBody) {
64
86
  let findOptions = {
65
87
  load: () => [],
@@ -219,25 +241,48 @@ export class DataApi {
219
241
  response.error(err);
220
242
  }
221
243
  }
244
+ async updateMany(response, request, body) {
245
+ try {
246
+ return await doTransaction(this.remult, async () => {
247
+ let updated = 0;
248
+ for await (const x of this.repository.query({
249
+ where: await this.buildWhere(request, body.where),
250
+ include: this.includeNone(),
251
+ })) {
252
+ await this.actualUpdate(x, body.set);
253
+ updated++;
254
+ }
255
+ response.success({ updated });
256
+ });
257
+ }
258
+ catch (err) {
259
+ response.error(err);
260
+ }
261
+ }
262
+ async actualUpdate(row, body) {
263
+ let ref = this.repository.getEntityRef(row);
264
+ await ref._updateEntityBasedOnApi(body);
265
+ if (!ref.apiUpdateAllowed) {
266
+ throw new ForbiddenError();
267
+ }
268
+ await ref.save();
269
+ return ref;
270
+ }
222
271
  async put(response, id, body) {
223
272
  await this.doOnId(response, id, async (row) => {
224
- let ref = this.repository.getEntityRef(row);
225
- await ref._updateEntityBasedOnApi(body);
226
- if (!ref.apiUpdateAllowed) {
227
- response.forbidden();
228
- return;
229
- }
230
- await ref.save();
273
+ const ref = await this.actualUpdate(row, body);
231
274
  response.success(ref.toApiJson());
232
275
  });
233
276
  }
277
+ async actualDelete(row) {
278
+ if (!this.repository.getEntityRef(row).apiDeleteAllowed) {
279
+ throw new ForbiddenError();
280
+ }
281
+ await this.repository.getEntityRef(row).delete();
282
+ }
234
283
  async delete(response, id) {
235
284
  await this.doOnId(response, id, async (row) => {
236
- if (!this.repository.getEntityRef(row).apiDeleteAllowed) {
237
- response.forbidden();
238
- return;
239
- }
240
- await this.repository.getEntityRef(row).delete();
285
+ await this.actualDelete(row);
241
286
  response.deleted();
242
287
  });
243
288
  }
@@ -3,6 +3,7 @@ import { dbNamesOf, isDbReadonly, } from '../filter/filter-consumer-bridge-to-sq
3
3
  import { Filter, customDatabaseFilterToken, } from '../filter/filter-interfaces.js';
4
4
  export class ArrayEntityDataProvider {
5
5
  entity;
6
+ rows;
6
7
  static rawFilter(filter) {
7
8
  return {
8
9
  [customDatabaseFilterToken]: {
@@ -10,14 +11,9 @@ export class ArrayEntityDataProvider {
10
11
  },
11
12
  };
12
13
  }
13
- //@internal
14
- rows;
15
14
  constructor(entity, rows) {
16
15
  this.entity = entity;
17
- if (rows === undefined)
18
- this.rows = [];
19
- else
20
- this.rows = rows;
16
+ this.rows = rows;
21
17
  }
22
18
  //@internal
23
19
  __names;
@@ -26,7 +22,7 @@ export class ArrayEntityDataProvider {
26
22
  if (this.__names)
27
23
  return this.__names;
28
24
  this.__names = await dbNamesOf(this.entity, (x) => x);
29
- for (const r of this.rows) {
25
+ for (const r of this.rows()) {
30
26
  this.verifyThatRowHasAllNotNullColumns(r, this.__names);
31
27
  }
32
28
  return this.__names;
@@ -53,7 +49,7 @@ export class ArrayEntityDataProvider {
53
49
  }
54
50
  }
55
51
  async count(where) {
56
- let rows = this.rows;
52
+ let rows = this.rows();
57
53
  const names = await this.init();
58
54
  let j = 0;
59
55
  for (let i = 0; i < rows.length; i++) {
@@ -70,7 +66,7 @@ export class ArrayEntityDataProvider {
70
66
  return j;
71
67
  }
72
68
  async find(options) {
73
- let rows = this.rows;
69
+ let rows = this.rows();
74
70
  const dbNames = await this.init();
75
71
  if (options) {
76
72
  if (options.where) {
@@ -122,8 +118,8 @@ export class ArrayEntityDataProvider {
122
118
  const names = await this.init();
123
119
  let idMatches = this.idMatches(id, names);
124
120
  let keys = Object.keys(data);
125
- for (let i = 0; i < this.rows.length; i++) {
126
- let r = this.rows[i];
121
+ for (let i = 0; i < this.rows().length; i++) {
122
+ let r = this.rows()[i];
127
123
  if (idMatches(r)) {
128
124
  let newR = { ...r };
129
125
  for (const f of this.entity.fields) {
@@ -134,8 +130,8 @@ export class ArrayEntityDataProvider {
134
130
  }
135
131
  }
136
132
  this.verifyThatRowHasAllNotNullColumns(newR, names);
137
- this.rows[i] = newR;
138
- return Promise.resolve(this.translateFromJson(this.rows[i], names));
133
+ this.rows()[i] = newR;
134
+ return Promise.resolve(this.translateFromJson(this.rows()[i], names));
139
135
  }
140
136
  }
141
137
  throw new Error("couldn't find id to update: " + id);
@@ -143,9 +139,9 @@ export class ArrayEntityDataProvider {
143
139
  async delete(id) {
144
140
  const names = await this.init();
145
141
  let idMatches = this.idMatches(id, names);
146
- for (let i = 0; i < this.rows.length; i++) {
147
- if (idMatches(this.rows[i])) {
148
- this.rows.splice(i, 1);
142
+ for (let i = 0; i < this.rows().length; i++) {
143
+ if (idMatches(this.rows()[i])) {
144
+ this.rows().splice(i, 1);
149
145
  return Promise.resolve();
150
146
  }
151
147
  }
@@ -158,19 +154,19 @@ export class ArrayEntityDataProvider {
158
154
  if (!(idf instanceof CompoundIdField)) {
159
155
  if (idf.valueConverter.fieldTypeInDb === 'autoincrement') {
160
156
  j[idf.key] = 1;
161
- for (const row of this.rows) {
157
+ for (const row of this.rows()) {
162
158
  if (row[idf.key] >= j[idf.key])
163
159
  j[idf.key] = row[idf.key] + 1;
164
160
  }
165
161
  }
166
162
  if (j[idf.key])
167
- this.rows.forEach((i) => {
163
+ this.rows().forEach((i) => {
168
164
  if (j[idf.key] == i[idf.key])
169
165
  throw Error('id already exists');
170
166
  });
171
167
  }
172
168
  this.verifyThatRowHasAllNotNullColumns(j, names);
173
- this.rows.push(j);
169
+ this.rows().push(j);
174
170
  return Promise.resolve(this.translateFromJson(j, names));
175
171
  }
176
172
  }
@@ -1,14 +1,21 @@
1
1
  import { ArrayEntityDataProvider } from './array-entity-data-provider.js';
2
2
  export class InMemoryDataProvider {
3
3
  async transaction(action) {
4
- await action(this);
4
+ let before = JSON.stringify(this.rows);
5
+ try {
6
+ await action(this);
7
+ }
8
+ catch (e) {
9
+ this.rows = JSON.parse(before);
10
+ throw e;
11
+ }
5
12
  }
6
13
  rows = {};
7
14
  getEntityDataProvider(entity) {
8
15
  let name = entity.key;
9
16
  if (!this.rows[name])
10
17
  this.rows[name] = [];
11
- return new ArrayEntityDataProvider(entity, this.rows[name]);
18
+ return new ArrayEntityDataProvider(entity, () => this.rows[name]);
12
19
  }
13
20
  toString() {
14
21
  return 'InMemoryDataProvider';
@@ -24,7 +24,7 @@ class JsonEntityDataProvider {
24
24
  let s = await this.helper.getItem(dbName);
25
25
  if (s)
26
26
  data = JSON.parse(s);
27
- let dp = new ArrayEntityDataProvider(this.entity, data);
27
+ let dp = new ArrayEntityDataProvider(this.entity, () => data);
28
28
  return what(dp, async () => await this.helper.setItem(dbName, JSON.stringify(data, undefined, 2)));
29
29
  }
30
30
  p = Promise.resolve();
@@ -119,18 +119,27 @@ export class RestEntityDataProvider {
119
119
  const { run } = this.buildFindRequest({ where });
120
120
  return run('count').then((r) => +r.count);
121
121
  }
122
+ async deleteMany(where) {
123
+ const { run } = this.buildFindRequest({ where }, true);
124
+ return run('deleteMany').then((r) => +r.deleted);
125
+ }
126
+ async updateMany(where, data) {
127
+ const { run } = this.buildFindRequest({ where }, true);
128
+ return run('updateMany', { set: this.toJsonOfIncludedKeys(data) }).then((r) => +r.updated);
129
+ }
122
130
  find(options) {
123
131
  let { run } = this.buildFindRequest(options);
124
132
  return run().then((x) => x.map((y) => this.translateFromJson(y)));
125
133
  }
126
134
  //@internal
127
- buildFindRequest(options) {
135
+ buildFindRequest(options, forcePost = false) {
128
136
  let url = new UrlBuilder(this.url());
129
137
  let filterObject;
130
138
  if (options) {
131
139
  if (options.where) {
132
140
  filterObject = options.where.toJson(); // options.where.__applyToConsumer(new FilterConsumnerBridgeToUrlBuilder(url));
133
- if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
141
+ if (!forcePost &&
142
+ addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
134
143
  filterObject = undefined;
135
144
  }
136
145
  if (options.orderBy && options.orderBy.Segments) {
@@ -157,15 +166,19 @@ export class RestEntityDataProvider {
157
166
  if (options.page)
158
167
  url.add('_page', options.page);
159
168
  }
160
- const run = (action) => {
169
+ const run = (action, body) => {
161
170
  let u = new UrlBuilder(url.url);
162
171
  if (!action && filterObject) {
163
172
  action = 'get';
164
173
  }
165
174
  if (action)
166
175
  u.add('__action', action);
167
- if (filterObject) {
168
- return this.http().post(u.url, filterObject);
176
+ if (filterObject || forcePost) {
177
+ if (!body)
178
+ body = filterObject;
179
+ else
180
+ body = { ...body, where: filterObject };
181
+ return this.http().post(u.url, body);
169
182
  }
170
183
  else
171
184
  return this.http().get(u.url);
@@ -187,15 +200,18 @@ export class RestEntityDataProvider {
187
200
  };
188
201
  }
189
202
  update(id, data) {
203
+ return this.http()
204
+ .put(this.url() + '/' + encodeURIComponent(id), this.toJsonOfIncludedKeys(data))
205
+ .then((y) => this.translateFromJson(y));
206
+ }
207
+ toJsonOfIncludedKeys(data) {
190
208
  let result = {};
191
209
  let keys = Object.keys(data);
192
210
  for (const col of this.entity.fields) {
193
211
  if (keys.includes(col.key))
194
212
  result[col.key] = col.valueConverter.toJson(data[col.key]);
195
213
  }
196
- return this.http()
197
- .put(this.url() + '/' + encodeURIComponent(id), result)
198
- .then((y) => this.translateFromJson(y));
214
+ return result;
199
215
  }
200
216
  delete(id) {
201
217
  return this.http().delete(this.url() + '/' + encodeURIComponent(id));
@@ -205,6 +221,11 @@ export class RestEntityDataProvider {
205
221
  .post(this.url(), this.translateToJson(data))
206
222
  .then((y) => this.translateFromJson(y));
207
223
  }
224
+ insertMany(data) {
225
+ return this.http()
226
+ .post(this.url(), data.map((data) => this.translateToJson(data)))
227
+ .then((y) => y.map((y) => this.translateFromJson(y)));
228
+ }
208
229
  }
209
230
  export class RestDataProviderHttpProviderUsingFetch {
210
231
  fetch;
@@ -208,11 +208,30 @@ export class RepositoryImplementation {
208
208
  }
209
209
  async insert(entity) {
210
210
  if (Array.isArray(entity)) {
211
- let r = [];
212
- for (const item of entity) {
213
- r.push(await this.insert(item));
211
+ if (this.dataProvider.isProxy) {
212
+ let refs = [];
213
+ let raw = [];
214
+ for (const item of entity) {
215
+ let ref = getEntityRef(entity, false);
216
+ if (ref) {
217
+ if (!ref.isNew())
218
+ throw 'Item is not new';
219
+ }
220
+ else {
221
+ ref = (await this.getEntityRef(this.create(item)));
222
+ }
223
+ refs.push(ref);
224
+ raw.push(await ref.buildDtoForInsert());
225
+ }
226
+ return Promise.all((await this.edp.insertMany(raw)).map((item, i) => refs[i].processInsertResponseDto(item)));
227
+ }
228
+ else {
229
+ let r = [];
230
+ for (const item of entity) {
231
+ r.push(await this.insert(item));
232
+ }
233
+ return r;
214
234
  }
215
- return r;
216
235
  }
217
236
  else {
218
237
  let ref = getEntityRef(entity, false);
@@ -250,6 +269,20 @@ export class RepositoryImplementation {
250
269
  }
251
270
  }
252
271
  }
272
+ async updateMany(where, set) {
273
+ if (this.dataProvider.isProxy) {
274
+ return this.edp.updateMany(await this.translateWhereToFilter(where), set);
275
+ }
276
+ else {
277
+ let updated = 0;
278
+ for await (const item of this.query({ where })) {
279
+ assign(item, set);
280
+ await getEntityRef(item).save();
281
+ updated++;
282
+ }
283
+ return updated;
284
+ }
285
+ }
253
286
  async update(id, entity) {
254
287
  {
255
288
  let ref = getEntityRef(entity, false);
@@ -602,6 +635,19 @@ export class RepositoryImplementation {
602
635
  async count(where) {
603
636
  return this.edp.count(await this.translateWhereToFilter(where));
604
637
  }
638
+ async deleteMany(where) {
639
+ if (this.dataProvider.isProxy) {
640
+ return this.edp.deleteMany(await this.translateWhereToFilter(where));
641
+ }
642
+ else {
643
+ let deleted = 0;
644
+ for await (const item of this.query({ where })) {
645
+ await getEntityRef(item).delete();
646
+ deleted++;
647
+ }
648
+ return deleted;
649
+ }
650
+ }
605
651
  cache = new Map();
606
652
  async findOne(options, skipOrderByAndLimit = false) {
607
653
  let r;
@@ -1273,6 +1319,27 @@ export class rowHelperImplementation extends rowHelperBase {
1273
1319
  this._saving = false;
1274
1320
  }
1275
1321
  }
1322
+ async processInsertResponseDto(updatedRow) {
1323
+ await this.loadDataFrom(updatedRow);
1324
+ this.saveOriginalData();
1325
+ this._isNew = false;
1326
+ return this.instance;
1327
+ }
1328
+ async buildDtoForInsert() {
1329
+ await this.__validateEntity();
1330
+ this.__assertValidity();
1331
+ let d = this.copyDataToObject(this.isNew());
1332
+ let ignoreKeys = [];
1333
+ for (const field of this.metadata.fields) {
1334
+ if (field.dbReadOnly) {
1335
+ d[field.key] = undefined;
1336
+ ignoreKeys.push(field.key);
1337
+ let f = this.fields.find(field);
1338
+ f.value = f.originalValue;
1339
+ }
1340
+ }
1341
+ return d;
1342
+ }
1276
1343
  buildLifeCycleEvent(preventDefault = () => { }) {
1277
1344
  const self = this;
1278
1345
  return {
@@ -13,17 +13,12 @@ export {};
13
13
 
14
14
 
15
15
  //p1 - add section to Fields doc, explaining field type in db
16
- /*p1 - JYC - Delete many, update many, insert many
17
- //p1 - @jycouet regarding required - are you ok with leaving '' as not a good value? (0 will be a good value)
18
-
19
- - post + __action=delet
20
- */
21
- //p1 - JYC - there should be a required that says not null or undefined, but space, 0 etc... should be ok - NOAM- check if removing zero is good enough
22
16
  /*y1 - Talk JYC - JYC - add some integrity checks on delete
23
17
  - soft delete
24
18
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
25
19
 
26
20
  */
21
+ //y1 תגיד - updateMany צריך להחזיר את השורות שעודכנו (כמו update או insert) או כמה שורות עודכנו (כמו deleteMany)
27
22
  /*y1 currency.ts:10 Uncaught TypeError: Currency_1 is not a constructor
28
23
 
29
24
  @ValueListFieldType()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.25.7-exp.1",
3
+ "version": "0.25.7-exp.2",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
package/src/data-api.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Remult } from './context.js';
2
2
  import type { ErrorInfo } from './data-interfaces.js';
3
3
  import type { FindOptions, Repository } from './remult3/remult3.js';
4
+ import type { rowHelperImplementation } from './remult3/RepositoryImplementation.js';
4
5
  export declare class DataApi<T = any> {
5
6
  private repository;
6
7
  private remult;
@@ -10,6 +11,7 @@ export declare class DataApi<T = any> {
10
11
  static defaultGetLimit: number;
11
12
  get(response: DataApiResponse, id: any): Promise<void>;
12
13
  count(response: DataApiResponse, request: DataApiRequest, filterBody?: any): Promise<void>;
14
+ deleteMany(response: DataApiResponse, request: DataApiRequest, filterBody?: any): Promise<void>;
13
15
  getArrayImpl(response: DataApiResponse, request: DataApiRequest, filterBody: any): Promise<{
14
16
  r: any[];
15
17
  findOptions: FindOptions<T>;
@@ -19,7 +21,10 @@ export declare class DataApi<T = any> {
19
21
  liveQuery(response: DataApiResponse, request: DataApiRequest, filterBody: any, serializeContext: () => Promise<any>, queryChannel: string): Promise<void>;
20
22
  private buildWhere;
21
23
  private doOnId;
24
+ updateMany(response: DataApiResponse, request: DataApiRequest, body?: any): Promise<void>;
25
+ actualUpdate(row: any, body: any): Promise<rowHelperImplementation<T>>;
22
26
  put(response: DataApiResponse, id: any, body: any): Promise<void>;
27
+ actualDelete(row: any): Promise<void>;
23
28
  delete(response: DataApiResponse, id: any): Promise<void>;
24
29
  post(response: DataApiResponse, body: any): Promise<void>;
25
30
  }