remult 0.25.8-exp.2 → 0.25.8-exp.4
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/esm/graphql.js +2 -2
- package/esm/server/expressBridge.js +80 -13
- package/esm/src/CompoundIdField.js +8 -2
- package/esm/src/data-api.js +45 -17
- package/esm/src/data-providers/rest-data-provider.js +17 -15
- package/esm/src/filter/filter-interfaces.js +31 -0
- package/esm/src/remult3/RepositoryImplementation.js +3 -1
- package/esm/src/remult3/remult3.js +2 -10
- package/esm/src/valueConverters.js +1 -1
- package/graphql.js +2 -4
- package/package.json +1 -1
- package/server/expressBridge.js +77 -15
- package/src/CompoundIdField.js +8 -2
- package/src/context.d.ts +2 -0
- package/src/data-api.d.ts +10 -6
- package/src/data-api.js +104 -67
- package/src/data-providers/rest-data-provider.js +26 -15
- package/src/filter/filter-interfaces.js +64 -12
- package/src/remult3/RepositoryImplementation.js +3 -1
- package/src/remult3/remult3.js +2 -10
- package/src/valueConverters.js +1 -1
package/esm/graphql.js
CHANGED
|
@@ -904,7 +904,7 @@ export function translateWhereToRestBody(fields, { where }) {
|
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
if (where.OR) {
|
|
907
|
-
result.OR = where.OR.map((where) => translateWhereToRestBody(fields, { where }));
|
|
907
|
+
result.OR = where.OR.map((where) => translateWhereToRestBody(fields, { where }).where);
|
|
908
908
|
}
|
|
909
|
-
return result;
|
|
909
|
+
return { where: result };
|
|
910
910
|
}
|
|
@@ -250,8 +250,8 @@ export class RemultServerImplementation {
|
|
|
250
250
|
console.info('[remult] ' + myRoute);
|
|
251
251
|
r.route(myRoute)
|
|
252
252
|
.get(this.process((c, req, res, orig) => dataApiFactory(c).httpGet(res, req, () => this.serializeContext(c))))
|
|
253
|
-
.put(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).
|
|
254
|
-
.delete(this.process(async (c, req, res, orig) => dataApiFactory(c).
|
|
253
|
+
.put(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).updateMany(res, req, await this.coreOptions.getRequestBody(orig))))
|
|
254
|
+
.delete(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).deleteMany(res, req, undefined)))
|
|
255
255
|
.post(this.process(async (c, req, res, _, __, orig) => dataApiFactory(c).httpPost(res, req, await this.coreOptions.getRequestBody(orig), () => this.serializeContext(c))));
|
|
256
256
|
r.route(myRoute + '/:id')
|
|
257
257
|
//@ts-ignore
|
|
@@ -467,6 +467,17 @@ export class RemultServerImplementation {
|
|
|
467
467
|
return secureBase(condition, def, item);
|
|
468
468
|
};
|
|
469
469
|
let apiPath = (spec.paths[this.options.rootPath + '/' + key] = {});
|
|
470
|
+
let itemInBody = {
|
|
471
|
+
requestBody: {
|
|
472
|
+
content: {
|
|
473
|
+
'application/json': {
|
|
474
|
+
schema: {
|
|
475
|
+
$ref: '#/components/schemas/' + mutationKey,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
};
|
|
470
481
|
let apiPathWithId = (spec.paths[this.options.rootPath + '/' + key + '/{id}'] = {});
|
|
471
482
|
//https://github.com/2fd/open-api.d.ts
|
|
472
483
|
apiPath.get = secure(meta.options.allowApiRead, true, {
|
|
@@ -519,6 +530,73 @@ export class RemultServerImplementation {
|
|
|
519
530
|
},
|
|
520
531
|
},
|
|
521
532
|
});
|
|
533
|
+
apiPath.delete = secure(meta.options.allowApiDelete, true, {
|
|
534
|
+
description: 'deletes row of ' +
|
|
535
|
+
key +
|
|
536
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)',
|
|
537
|
+
parameters: [...parameters],
|
|
538
|
+
responses: {
|
|
539
|
+
'400': {
|
|
540
|
+
description: 'Error: Bad Request',
|
|
541
|
+
content: {
|
|
542
|
+
'application/json': {
|
|
543
|
+
schema: {
|
|
544
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
'200': {
|
|
550
|
+
description: 'returns the number of deleted rows ' + key,
|
|
551
|
+
content: {
|
|
552
|
+
'application/json': {
|
|
553
|
+
schema: {
|
|
554
|
+
type: 'object',
|
|
555
|
+
properties: {
|
|
556
|
+
deleted: {
|
|
557
|
+
type: 'number',
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
apiPath.put = secure(meta.options.allowApiDelete, true, {
|
|
567
|
+
description: 'updates row of ' +
|
|
568
|
+
key +
|
|
569
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)',
|
|
570
|
+
parameters: [...parameters],
|
|
571
|
+
...itemInBody,
|
|
572
|
+
responses: {
|
|
573
|
+
'400': {
|
|
574
|
+
description: 'Error: Bad Request',
|
|
575
|
+
content: {
|
|
576
|
+
'application/json': {
|
|
577
|
+
schema: {
|
|
578
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
'200': {
|
|
584
|
+
description: 'returns the number of updated rows ' + key,
|
|
585
|
+
content: {
|
|
586
|
+
'application/json': {
|
|
587
|
+
schema: {
|
|
588
|
+
type: 'object',
|
|
589
|
+
properties: {
|
|
590
|
+
updated: {
|
|
591
|
+
type: 'number',
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
});
|
|
522
600
|
let idParameter = {
|
|
523
601
|
name: 'id',
|
|
524
602
|
in: 'path',
|
|
@@ -526,17 +604,6 @@ export class RemultServerImplementation {
|
|
|
526
604
|
required: true,
|
|
527
605
|
schema: { type: 'string' },
|
|
528
606
|
};
|
|
529
|
-
let itemInBody = {
|
|
530
|
-
requestBody: {
|
|
531
|
-
content: {
|
|
532
|
-
'application/json': {
|
|
533
|
-
schema: {
|
|
534
|
-
$ref: '#/components/schemas/' + mutationKey,
|
|
535
|
-
},
|
|
536
|
-
},
|
|
537
|
-
},
|
|
538
|
-
},
|
|
539
|
-
};
|
|
540
607
|
apiPath.post = secure(meta.options.allowApiInsert, false, {
|
|
541
608
|
//"summary": "insert a " + key,
|
|
542
609
|
//"description": "insert a " + key,
|
|
@@ -22,11 +22,17 @@ export class CompoundIdField {
|
|
|
22
22
|
return Promise.resolve('');
|
|
23
23
|
}
|
|
24
24
|
getId(instance) {
|
|
25
|
+
let get = (field) => {
|
|
26
|
+
return instance[field.key];
|
|
27
|
+
};
|
|
28
|
+
if (typeof instance === 'function') {
|
|
29
|
+
get = instance;
|
|
30
|
+
}
|
|
25
31
|
let r = '';
|
|
26
32
|
this.fields.forEach((c) => {
|
|
27
33
|
if (r.length > 0)
|
|
28
34
|
r += ',';
|
|
29
|
-
r +=
|
|
35
|
+
r += c.valueConverter.toJson(get(c));
|
|
30
36
|
});
|
|
31
37
|
return r;
|
|
32
38
|
}
|
|
@@ -49,7 +55,7 @@ export class CompoundIdField {
|
|
|
49
55
|
let val = value.toString();
|
|
50
56
|
let id = val.split(',');
|
|
51
57
|
this.fields.forEach((c, i) => {
|
|
52
|
-
result[c.key] = id[i];
|
|
58
|
+
result[c.key] = c.valueConverter.fromJson(id[i]);
|
|
53
59
|
});
|
|
54
60
|
return result;
|
|
55
61
|
}
|
package/esm/src/data-api.js
CHANGED
|
@@ -23,17 +23,31 @@ export class DataApi {
|
|
|
23
23
|
}
|
|
24
24
|
async httpPost(res, req, body, serializeContext) {
|
|
25
25
|
const action = req?.get('__action');
|
|
26
|
-
|
|
26
|
+
function validateWhereInBody() {
|
|
27
|
+
if (!body?.where) {
|
|
28
|
+
throw {
|
|
29
|
+
message: `POST with action ${action} must have a where clause in the body`,
|
|
30
|
+
httpStatusCode: 400,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (action?.startsWith(liveQueryAction)) {
|
|
35
|
+
validateWhereInBody();
|
|
27
36
|
return this.liveQuery(res, req, body, serializeContext, action.substring(liveQueryAction.length));
|
|
37
|
+
}
|
|
28
38
|
switch (action) {
|
|
29
39
|
case 'get':
|
|
40
|
+
validateWhereInBody();
|
|
30
41
|
return this.getArray(res, req, body);
|
|
31
42
|
case 'count':
|
|
43
|
+
validateWhereInBody();
|
|
32
44
|
return this.count(res, req, body);
|
|
33
45
|
case 'deleteMany':
|
|
46
|
+
validateWhereInBody();
|
|
34
47
|
return this.deleteMany(res, req, body);
|
|
35
48
|
case 'updateMany':
|
|
36
|
-
|
|
49
|
+
validateWhereInBody();
|
|
50
|
+
return this.updateManyImplementation(res, req, body);
|
|
37
51
|
case 'endLiveQuery':
|
|
38
52
|
await this.remult.liveQueryStorage.remove(body.id);
|
|
39
53
|
res.success('ok');
|
|
@@ -50,26 +64,28 @@ export class DataApi {
|
|
|
50
64
|
}
|
|
51
65
|
await this.doOnId(response, id, async (row) => response.success(this.repository.getEntityRef(row).toApiJson()));
|
|
52
66
|
}
|
|
53
|
-
async count(response, request,
|
|
67
|
+
async count(response, request, body) {
|
|
54
68
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
55
69
|
response.forbidden();
|
|
56
70
|
return;
|
|
57
71
|
}
|
|
58
72
|
try {
|
|
59
73
|
response.success({
|
|
60
|
-
count: +(await this.repository.count(await this.buildWhere(request,
|
|
74
|
+
count: +(await this.repository.count(await this.buildWhere(request, body))),
|
|
61
75
|
});
|
|
62
76
|
}
|
|
63
77
|
catch (err) {
|
|
64
78
|
response.error(err, this.repository.metadata);
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
|
-
async deleteMany(response, request,
|
|
81
|
+
async deleteMany(response, request, body) {
|
|
68
82
|
try {
|
|
83
|
+
let deleted = 0;
|
|
84
|
+
let where = await this.buildWhere(request, body);
|
|
85
|
+
Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
|
|
69
86
|
return await doTransaction(this.remult, async () => {
|
|
70
|
-
let deleted = 0;
|
|
71
87
|
for await (const x of this.repository.query({
|
|
72
|
-
where
|
|
88
|
+
where,
|
|
73
89
|
include: this.includeNone(),
|
|
74
90
|
})) {
|
|
75
91
|
await this.actualDelete(x);
|
|
@@ -82,12 +98,12 @@ export class DataApi {
|
|
|
82
98
|
response.error(err, this.repository.metadata);
|
|
83
99
|
}
|
|
84
100
|
}
|
|
85
|
-
async getArrayImpl(response, request,
|
|
101
|
+
async getArrayImpl(response, request, body) {
|
|
86
102
|
let findOptions = {
|
|
87
103
|
load: () => [],
|
|
88
104
|
include: this.includeNone(),
|
|
89
105
|
};
|
|
90
|
-
findOptions.where = await this.buildWhere(request,
|
|
106
|
+
findOptions.where = await this.buildWhere(request, body);
|
|
91
107
|
if (request) {
|
|
92
108
|
let sort = request.get('_sort');
|
|
93
109
|
if (sort != undefined) {
|
|
@@ -146,13 +162,13 @@ export class DataApi {
|
|
|
146
162
|
}
|
|
147
163
|
return include;
|
|
148
164
|
}
|
|
149
|
-
async getArray(response, request,
|
|
165
|
+
async getArray(response, request, body) {
|
|
150
166
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
151
167
|
response.forbidden();
|
|
152
168
|
return;
|
|
153
169
|
}
|
|
154
170
|
try {
|
|
155
|
-
const { r } = await this.getArrayImpl(response, request,
|
|
171
|
+
const { r } = await this.getArrayImpl(response, request, body);
|
|
156
172
|
response.success(r);
|
|
157
173
|
}
|
|
158
174
|
catch (err) {
|
|
@@ -162,13 +178,13 @@ export class DataApi {
|
|
|
162
178
|
response.error(err, this.repository.metadata);
|
|
163
179
|
}
|
|
164
180
|
}
|
|
165
|
-
async liveQuery(response, request,
|
|
181
|
+
async liveQuery(response, request, body, serializeContext, queryChannel) {
|
|
166
182
|
if (!this.repository.metadata.apiReadAllowed) {
|
|
167
183
|
response.forbidden();
|
|
168
184
|
return;
|
|
169
185
|
}
|
|
170
186
|
try {
|
|
171
|
-
const r = await this.getArrayImpl(response, request,
|
|
187
|
+
const r = await this.getArrayImpl(response, request, body);
|
|
172
188
|
const data = {
|
|
173
189
|
requestJson: await serializeContext(),
|
|
174
190
|
findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
|
|
@@ -188,7 +204,7 @@ export class DataApi {
|
|
|
188
204
|
response.error(err, this.repository.metadata);
|
|
189
205
|
}
|
|
190
206
|
}
|
|
191
|
-
async buildWhere(request,
|
|
207
|
+
async buildWhere(request, body) {
|
|
192
208
|
var where = [];
|
|
193
209
|
if (this.repository.metadata.options.apiPrefilter) {
|
|
194
210
|
if (typeof this.repository.metadata.options.apiPrefilter === 'function')
|
|
@@ -208,8 +224,8 @@ export class DataApi {
|
|
|
208
224
|
},
|
|
209
225
|
}));
|
|
210
226
|
}
|
|
211
|
-
if (
|
|
212
|
-
where.push(Filter.entityFilterFromJson(this.repository.metadata,
|
|
227
|
+
if (body?.where)
|
|
228
|
+
where.push(Filter.entityFilterFromJson(this.repository.metadata, body.where));
|
|
213
229
|
return { $and: where };
|
|
214
230
|
}
|
|
215
231
|
async doOnId(response, id, what) {
|
|
@@ -245,11 +261,23 @@ export class DataApi {
|
|
|
245
261
|
}
|
|
246
262
|
}
|
|
247
263
|
async updateMany(response, request, body) {
|
|
264
|
+
const action = request?.get('__action');
|
|
265
|
+
if (action == 'emptyId') {
|
|
266
|
+
return this.put(response, '', body);
|
|
267
|
+
}
|
|
268
|
+
let where = await this.buildWhere(request, body);
|
|
269
|
+
Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
|
|
270
|
+
return this.updateManyImplementation(response, request, {
|
|
271
|
+
where: undefined,
|
|
272
|
+
set: body,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async updateManyImplementation(response, request, body) {
|
|
248
276
|
try {
|
|
249
277
|
return await doTransaction(this.remult, async () => {
|
|
250
278
|
let updated = 0;
|
|
251
279
|
for await (const x of this.repository.query({
|
|
252
|
-
where: await this.buildWhere(request, body
|
|
280
|
+
where: await this.buildWhere(request, body),
|
|
253
281
|
include: this.includeNone(),
|
|
254
282
|
})) {
|
|
255
283
|
await this.actualUpdate(x, body.set);
|
|
@@ -120,26 +120,27 @@ export class RestEntityDataProvider {
|
|
|
120
120
|
return run('count').then((r) => +r.count);
|
|
121
121
|
}
|
|
122
122
|
async deleteMany(where) {
|
|
123
|
-
const { run } = this.buildFindRequest({ where },
|
|
123
|
+
const { run } = this.buildFindRequest({ where }, 'delete');
|
|
124
124
|
return run('deleteMany').then((r) => +r.deleted);
|
|
125
125
|
}
|
|
126
126
|
async updateMany(where, data) {
|
|
127
|
-
const { run } = this.buildFindRequest({ where },
|
|
128
|
-
return run('updateMany',
|
|
127
|
+
const { run } = this.buildFindRequest({ where }, 'put');
|
|
128
|
+
return run('updateMany', this.toJsonOfIncludedKeys(data)).then((r) => +r.updated);
|
|
129
129
|
}
|
|
130
130
|
find(options) {
|
|
131
131
|
let { run } = this.buildFindRequest(options);
|
|
132
132
|
return run().then((x) => x.map((y) => this.translateFromJson(y)));
|
|
133
133
|
}
|
|
134
134
|
//@internal
|
|
135
|
-
buildFindRequest(options,
|
|
135
|
+
buildFindRequest(options, method) {
|
|
136
|
+
if (!method)
|
|
137
|
+
method = 'get';
|
|
136
138
|
let url = new UrlBuilder(this.url());
|
|
137
139
|
let filterObject;
|
|
138
140
|
if (options) {
|
|
139
141
|
if (options.where) {
|
|
140
142
|
filterObject = options.where.toJson(); // options.where.__applyToConsumer(new FilterConsumnerBridgeToUrlBuilder(url));
|
|
141
|
-
if (
|
|
142
|
-
addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
|
|
143
|
+
if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
|
|
143
144
|
filterObject = undefined;
|
|
144
145
|
}
|
|
145
146
|
if (options.orderBy && options.orderBy.Segments) {
|
|
@@ -173,15 +174,12 @@ export class RestEntityDataProvider {
|
|
|
173
174
|
}
|
|
174
175
|
if (action)
|
|
175
176
|
u.add('__action', action);
|
|
176
|
-
if (filterObject
|
|
177
|
-
|
|
178
|
-
body = filterObject;
|
|
179
|
-
else
|
|
180
|
-
body = { ...body, where: filterObject };
|
|
177
|
+
if (filterObject) {
|
|
178
|
+
body = { set: body, where: filterObject };
|
|
181
179
|
return this.http().post(u.url, body);
|
|
182
180
|
}
|
|
183
181
|
else
|
|
184
|
-
return this.http()
|
|
182
|
+
return this.http()[method](u.url, body);
|
|
185
183
|
};
|
|
186
184
|
return {
|
|
187
185
|
createKey: () => JSON.stringify({ url, filterObject }),
|
|
@@ -201,7 +199,8 @@ export class RestEntityDataProvider {
|
|
|
201
199
|
}
|
|
202
200
|
update(id, data) {
|
|
203
201
|
return this.http()
|
|
204
|
-
.put(this.url() +
|
|
202
|
+
.put(this.url() +
|
|
203
|
+
(id != '' ? '/' + encodeURIComponent(id) : '?__action=emptyId'), this.toJsonOfIncludedKeys(data))
|
|
205
204
|
.then((y) => this.translateFromJson(y));
|
|
206
205
|
}
|
|
207
206
|
toJsonOfIncludedKeys(data) {
|
|
@@ -213,8 +212,11 @@ export class RestEntityDataProvider {
|
|
|
213
212
|
}
|
|
214
213
|
return result;
|
|
215
214
|
}
|
|
216
|
-
delete(id) {
|
|
217
|
-
|
|
215
|
+
async delete(id) {
|
|
216
|
+
if (id == '')
|
|
217
|
+
await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)));
|
|
218
|
+
else
|
|
219
|
+
return this.http().delete(this.url() + '/' + encodeURIComponent(id));
|
|
218
220
|
}
|
|
219
221
|
insert(data) {
|
|
220
222
|
return this.http()
|
|
@@ -6,6 +6,37 @@ import { getRelationFieldInfo } from '../remult3/relationInfoMember.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export class Filter {
|
|
8
8
|
apply;
|
|
9
|
+
//@internal
|
|
10
|
+
static throwErrorIfFilterIsEmpty(where, methodName) {
|
|
11
|
+
if (Filter.isFilterEmpty(where)) {
|
|
12
|
+
throw {
|
|
13
|
+
message: `${methodName}: requires a filter to protect against accidental delete/update of all rows`,
|
|
14
|
+
httpStatusCode: 400,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//@internal
|
|
19
|
+
static isFilterEmpty(where) {
|
|
20
|
+
if (where.$and) {
|
|
21
|
+
for (const a of where.$and) {
|
|
22
|
+
if (!Filter.isFilterEmpty(a)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (where.$or) {
|
|
28
|
+
for (const a of where.$or) {
|
|
29
|
+
if (Filter.isFilterEmpty(a)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (Object.keys(where).filter((x) => !['$or', '$and'].includes(x)).length == 0) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
9
40
|
static createCustom(rawFilterTranslator, key = '') {
|
|
10
41
|
let rawFilterInfo = { key: key, rawFilterTranslator };
|
|
11
42
|
return Object.assign((x) => {
|
|
@@ -270,6 +270,7 @@ export class RepositoryImplementation {
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
async updateMany(where, set) {
|
|
273
|
+
Filter.throwErrorIfFilterIsEmpty(where, 'updateMany');
|
|
273
274
|
if (this._dataProvider.isProxy) {
|
|
274
275
|
return this._edp.updateMany(await this._translateWhereToFilter(where), set);
|
|
275
276
|
}
|
|
@@ -636,6 +637,7 @@ export class RepositoryImplementation {
|
|
|
636
637
|
return this._edp.count(await this._translateWhereToFilter(where));
|
|
637
638
|
}
|
|
638
639
|
async deleteMany(where) {
|
|
640
|
+
Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
|
|
639
641
|
if (this._dataProvider.isProxy) {
|
|
640
642
|
return this._edp.deleteMany(await this._translateWhereToFilter(where));
|
|
641
643
|
}
|
|
@@ -1184,7 +1186,7 @@ export class rowHelperImplementation extends rowHelperBase {
|
|
|
1184
1186
|
return this.instance[y.key];
|
|
1185
1187
|
};
|
|
1186
1188
|
if (this.metadata.idMetadata.field instanceof CompoundIdField)
|
|
1187
|
-
return this.metadata.idMetadata.field.
|
|
1189
|
+
return this.metadata.idMetadata.field.getId(getVal);
|
|
1188
1190
|
return getVal(this.metadata.idMetadata.field);
|
|
1189
1191
|
}
|
|
1190
1192
|
saveMoreOriginalData() {
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
export {};
|
|
2
|
+
//p1 - should the remult api be deleteMany({id:[1,2]}) or deleteMany({where:{id:[1,2]}})
|
|
2
3
|
//p1 - in this video I'll use remult to turn a frontend app to a fullstack app
|
|
3
|
-
/*p1 - discuss using delete & put - with url query language for deleteMany and updateMany -
|
|
4
|
-
- put & delete, similar to get
|
|
5
|
-
- add where to count, deleteMany,updateMany,
|
|
6
|
-
- prevent delete all and update all - must have meaningful where.
|
|
7
|
-
- try forcing this also in typescript
|
|
8
|
-
|
|
9
|
-
- protect against deleting of all rows by mistake
|
|
10
|
-
- https://github.com/remult/remult/issues/221#issuecomment-2016519746
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
4
|
//y1 - getFields didn't work for kobi in the home component
|
|
5
|
+
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
14
6
|
//p1 - document offline support
|
|
15
7
|
//p1 - add section to Fields doc, explaining field type in db
|
|
16
8
|
//p1 - add section about union type
|
package/graphql.js
CHANGED
|
@@ -1081,10 +1081,8 @@ function translateWhereToRestBody(fields, _a) {
|
|
|
1081
1081
|
finally { if (e_7) throw e_7.error; }
|
|
1082
1082
|
}
|
|
1083
1083
|
if (where.OR) {
|
|
1084
|
-
result.OR = where.OR.map(function (where) {
|
|
1085
|
-
return translateWhereToRestBody(fields, { where: where });
|
|
1086
|
-
});
|
|
1084
|
+
result.OR = where.OR.map(function (where) { return translateWhereToRestBody(fields, { where: where }).where; });
|
|
1087
1085
|
}
|
|
1088
|
-
return result;
|
|
1086
|
+
return { where: result };
|
|
1089
1087
|
}
|
|
1090
1088
|
exports.translateWhereToRestBody = translateWhereToRestBody;
|
package/package.json
CHANGED
package/server/expressBridge.js
CHANGED
|
@@ -444,16 +444,16 @@ var RemultServerImplementation = /** @class */ (function () {
|
|
|
444
444
|
return tslib_1.__generator(this, function (_d) {
|
|
445
445
|
switch (_d.label) {
|
|
446
446
|
case 0:
|
|
447
|
-
_b = (_a = dataApiFactory(c)).
|
|
447
|
+
_b = (_a = dataApiFactory(c)).updateMany;
|
|
448
448
|
_c = [res,
|
|
449
|
-
|
|
449
|
+
req];
|
|
450
450
|
return [4 /*yield*/, this.coreOptions.getRequestBody(orig)];
|
|
451
451
|
case 1: return [2 /*return*/, _b.apply(_a, _c.concat([_d.sent()]))];
|
|
452
452
|
}
|
|
453
453
|
});
|
|
454
454
|
}); }))
|
|
455
|
-
.delete(this.process(function (c, req, res, orig) { return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) {
|
|
456
|
-
return [2 /*return*/, dataApiFactory(c).
|
|
455
|
+
.delete(this.process(function (c, req, res, _, __, orig) { return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) {
|
|
456
|
+
return [2 /*return*/, dataApiFactory(c).deleteMany(res, req, undefined)];
|
|
457
457
|
}); }); }))
|
|
458
458
|
.post(this.process(function (c, req, res, _, __, orig) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
459
459
|
var _a, _b, _c;
|
|
@@ -792,6 +792,17 @@ var RemultServerImplementation = /** @class */ (function () {
|
|
|
792
792
|
return secureBase(condition, def, item);
|
|
793
793
|
};
|
|
794
794
|
var apiPath = (spec.paths[this_2.options.rootPath + '/' + key] = {});
|
|
795
|
+
var itemInBody = {
|
|
796
|
+
requestBody: {
|
|
797
|
+
content: {
|
|
798
|
+
'application/json': {
|
|
799
|
+
schema: {
|
|
800
|
+
$ref: '#/components/schemas/' + mutationKey,
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
};
|
|
795
806
|
var apiPathWithId = (spec.paths[this_2.options.rootPath + '/' + key + '/{id}'] = {});
|
|
796
807
|
//https://github.com/2fd/open-api.d.ts
|
|
797
808
|
apiPath.get = secure(meta.options.allowApiRead, true, {
|
|
@@ -843,6 +854,68 @@ var RemultServerImplementation = /** @class */ (function () {
|
|
|
843
854
|
},
|
|
844
855
|
},
|
|
845
856
|
});
|
|
857
|
+
apiPath.delete = secure(meta.options.allowApiDelete, true, {
|
|
858
|
+
description: 'deletes row of ' +
|
|
859
|
+
key +
|
|
860
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)',
|
|
861
|
+
parameters: tslib_1.__spreadArray([], tslib_1.__read(parameters), false),
|
|
862
|
+
responses: {
|
|
863
|
+
'400': {
|
|
864
|
+
description: 'Error: Bad Request',
|
|
865
|
+
content: {
|
|
866
|
+
'application/json': {
|
|
867
|
+
schema: {
|
|
868
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
'200': {
|
|
874
|
+
description: 'returns the number of deleted rows ' + key,
|
|
875
|
+
content: {
|
|
876
|
+
'application/json': {
|
|
877
|
+
schema: {
|
|
878
|
+
type: 'object',
|
|
879
|
+
properties: {
|
|
880
|
+
deleted: {
|
|
881
|
+
type: 'number',
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
apiPath.put = secure(meta.options.allowApiDelete, true, tslib_1.__assign(tslib_1.__assign({ description: 'updates row of ' +
|
|
891
|
+
key +
|
|
892
|
+
'. supports filter operators. For more info on filtering [see this article](https://remult.dev/docs/rest-api.html#filter)', parameters: tslib_1.__spreadArray([], tslib_1.__read(parameters), false) }, itemInBody), { responses: {
|
|
893
|
+
'400': {
|
|
894
|
+
description: 'Error: Bad Request',
|
|
895
|
+
content: {
|
|
896
|
+
'application/json': {
|
|
897
|
+
schema: {
|
|
898
|
+
$ref: '#/components/schemas/InvalidResponse',
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
'200': {
|
|
904
|
+
description: 'returns the number of updated rows ' + key,
|
|
905
|
+
content: {
|
|
906
|
+
'application/json': {
|
|
907
|
+
schema: {
|
|
908
|
+
type: 'object',
|
|
909
|
+
properties: {
|
|
910
|
+
updated: {
|
|
911
|
+
type: 'number',
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
} }));
|
|
846
919
|
var idParameter = {
|
|
847
920
|
name: 'id',
|
|
848
921
|
in: 'path',
|
|
@@ -850,17 +923,6 @@ var RemultServerImplementation = /** @class */ (function () {
|
|
|
850
923
|
required: true,
|
|
851
924
|
schema: { type: 'string' },
|
|
852
925
|
};
|
|
853
|
-
var itemInBody = {
|
|
854
|
-
requestBody: {
|
|
855
|
-
content: {
|
|
856
|
-
'application/json': {
|
|
857
|
-
schema: {
|
|
858
|
-
$ref: '#/components/schemas/' + mutationKey,
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
},
|
|
862
|
-
},
|
|
863
|
-
};
|
|
864
926
|
apiPath.post = secure(meta.options.allowApiInsert, false, tslib_1.__assign(tslib_1.__assign({
|
|
865
927
|
//"summary": "insert a " + key,
|
|
866
928
|
//"description": "insert a " + key,
|
package/src/CompoundIdField.js
CHANGED
|
@@ -28,11 +28,17 @@ var CompoundIdField = /** @class */ (function () {
|
|
|
28
28
|
return Promise.resolve('');
|
|
29
29
|
};
|
|
30
30
|
CompoundIdField.prototype.getId = function (instance) {
|
|
31
|
+
var get = function (field) {
|
|
32
|
+
return instance[field.key];
|
|
33
|
+
};
|
|
34
|
+
if (typeof instance === 'function') {
|
|
35
|
+
get = instance;
|
|
36
|
+
}
|
|
31
37
|
var r = '';
|
|
32
38
|
this.fields.forEach(function (c) {
|
|
33
39
|
if (r.length > 0)
|
|
34
40
|
r += ',';
|
|
35
|
-
r +=
|
|
41
|
+
r += c.valueConverter.toJson(get(c));
|
|
36
42
|
});
|
|
37
43
|
return r;
|
|
38
44
|
};
|
|
@@ -48,7 +54,7 @@ var CompoundIdField = /** @class */ (function () {
|
|
|
48
54
|
var val = value.toString();
|
|
49
55
|
var id = val.split(',');
|
|
50
56
|
this.fields.forEach(function (c, i) {
|
|
51
|
-
result[c.key] = id[i];
|
|
57
|
+
result[c.key] = c.valueConverter.fromJson(id[i]);
|
|
52
58
|
});
|
|
53
59
|
return result;
|
|
54
60
|
};
|