@opra/sqb 0.33.13 → 1.0.0-alpha.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/cjs/{transform-filter.js → adapter-utils/parse-filter.js} +37 -12
- package/cjs/augmentation/datatype-factory.augmentation.js +75 -0
- package/cjs/augmentation/mixin-type.augmentation.js +5 -3
- package/cjs/index.js +3 -4
- package/cjs/sqb-adapter.js +66 -100
- package/cjs/sqb-collection-service.js +297 -0
- package/cjs/sqb-entity-service.js +444 -25
- package/cjs/sqb-singleton-service.js +180 -0
- package/esm/{transform-filter.js → adapter-utils/parse-filter.js} +36 -11
- package/esm/augmentation/datatype-factory.augmentation.js +73 -0
- package/esm/augmentation/mapped-type.augmentation.js +1 -1
- package/esm/augmentation/mixin-type.augmentation.js +6 -4
- package/esm/index.js +3 -4
- package/esm/sqb-adapter.js +66 -100
- package/esm/sqb-collection-service.js +293 -0
- package/esm/sqb-entity-service.js +444 -25
- package/esm/sqb-singleton-service.js +176 -0
- package/package.json +10 -9
- package/types/adapter-utils/parse-filter.d.ts +10 -0
- package/types/index.d.ts +3 -4
- package/types/sqb-adapter.d.ts +10 -8
- package/types/sqb-collection-service.d.ts +233 -0
- package/types/sqb-entity-service.d.ts +418 -18
- package/types/sqb-singleton-service.d.ts +137 -0
- package/cjs/augmentation/api-document-factory.augmentation.js +0 -20
- package/cjs/augmentation/type-document-factory.augmentation.js +0 -99
- package/cjs/sqb-collection.js +0 -80
- package/cjs/sqb-entity-service-base.js +0 -170
- package/cjs/sqb-singleton.js +0 -44
- package/cjs/transform-key-values.js +0 -14
- package/esm/augmentation/api-document-factory.augmentation.js +0 -18
- package/esm/augmentation/type-document-factory.augmentation.js +0 -97
- package/esm/sqb-collection.js +0 -76
- package/esm/sqb-entity-service-base.js +0 -166
- package/esm/sqb-singleton.js +0 -40
- package/esm/transform-key-values.js +0 -11
- package/types/augmentation/type-document-factory.augmentation.d.ts +0 -1
- package/types/sqb-collection.d.ts +0 -31
- package/types/sqb-entity-service-base.d.ts +0 -31
- package/types/sqb-singleton.d.ts +0 -18
- package/types/transform-filter.d.ts +0 -3
- package/types/transform-key-values.d.ts +0 -3
- /package/types/augmentation/{api-document-factory.augmentation.d.ts → datatype-factory.augmentation.d.ts} +0 -0
|
@@ -1,37 +1,456 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { InternalServerError } from '@opra/common';
|
|
2
|
+
import { ServiceBase } from '@opra/core';
|
|
3
|
+
import { EntityMetadata } from '@sqb/connect';
|
|
4
|
+
import { SQBAdapter } from './sqb-adapter.js';
|
|
5
|
+
/**
|
|
6
|
+
* @class SqbEntityService
|
|
7
|
+
* @template T - The data type class type of the resource
|
|
8
|
+
*/
|
|
9
|
+
export class SqbEntityService extends ServiceBase {
|
|
10
|
+
/**
|
|
11
|
+
* Constructs a new instance
|
|
12
|
+
*
|
|
13
|
+
* @param dataType - The data type of the returning results
|
|
14
|
+
* @param [options] - The options for the service.
|
|
15
|
+
* @constructor
|
|
16
|
+
*/
|
|
17
|
+
constructor(dataType, options) {
|
|
18
|
+
super();
|
|
19
|
+
this._encoders = {};
|
|
20
|
+
this._dataType_ = dataType;
|
|
21
|
+
this.db = options?.db;
|
|
22
|
+
this.$resourceName = options?.resourceName;
|
|
23
|
+
this.$commonFilter = this.$commonFilter || options?.commonFilter;
|
|
24
|
+
this.$interceptor = this.$interceptor || options?.interceptor;
|
|
6
25
|
}
|
|
7
|
-
|
|
8
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves the OPRA data type
|
|
28
|
+
*
|
|
29
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
30
|
+
*/
|
|
31
|
+
get dataType() {
|
|
32
|
+
if (!this._dataType)
|
|
33
|
+
this._dataType = this.context.document.node.getComplexType(this._dataType_);
|
|
34
|
+
return this._dataType;
|
|
9
35
|
}
|
|
10
|
-
|
|
11
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the Class of the data type
|
|
38
|
+
*
|
|
39
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
40
|
+
*/
|
|
41
|
+
get dataTypeClass() {
|
|
42
|
+
if (!this._dataTypeClass)
|
|
43
|
+
this._dataTypeClass = this.entityMetadata.ctor;
|
|
44
|
+
return this._dataTypeClass;
|
|
12
45
|
}
|
|
13
|
-
|
|
14
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves the SQB entity metadata object
|
|
48
|
+
*
|
|
49
|
+
* @throws {TypeError} If metadata is not available
|
|
50
|
+
*/
|
|
51
|
+
get entityMetadata() {
|
|
52
|
+
if (!this._entityMetadata) {
|
|
53
|
+
const t = this.dataType.ctor;
|
|
54
|
+
const metadata = EntityMetadata.get(t);
|
|
55
|
+
if (!metadata)
|
|
56
|
+
throw new TypeError(`Class (${t}) is not decorated with $Entity() decorator`);
|
|
57
|
+
this._entityMetadata = metadata;
|
|
58
|
+
}
|
|
59
|
+
return this._entityMetadata;
|
|
15
60
|
}
|
|
16
|
-
|
|
17
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves the resource name.
|
|
63
|
+
*
|
|
64
|
+
* @returns {string} The resource name.
|
|
65
|
+
* @throws {Error} If the collection name is not defined.
|
|
66
|
+
*/
|
|
67
|
+
getResourceName() {
|
|
68
|
+
const out = typeof this.$resourceName === 'function' ? this.$resourceName(this) : this.$resourceName || this.dataType.name;
|
|
69
|
+
if (out)
|
|
70
|
+
return out;
|
|
71
|
+
throw new Error('resourceName is not defined');
|
|
18
72
|
}
|
|
19
|
-
|
|
20
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Retrieves the encoder for the specified operation.
|
|
75
|
+
*
|
|
76
|
+
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
77
|
+
*/
|
|
78
|
+
getEncoder(operation) {
|
|
79
|
+
let encoder = this._encoders[operation];
|
|
80
|
+
if (encoder)
|
|
81
|
+
return encoder;
|
|
82
|
+
const options = { projection: '*' };
|
|
83
|
+
if (operation === 'update')
|
|
84
|
+
options.partial = 'deep';
|
|
85
|
+
const dataType = this.dataType;
|
|
86
|
+
encoder = dataType.generateCodec('encode', options);
|
|
87
|
+
this._encoders[operation] = encoder;
|
|
88
|
+
return encoder;
|
|
21
89
|
}
|
|
22
|
-
|
|
23
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Retrieves the decoder.
|
|
92
|
+
*/
|
|
93
|
+
getDecoder() {
|
|
94
|
+
let decoder = this._decoder;
|
|
95
|
+
if (decoder)
|
|
96
|
+
return decoder;
|
|
97
|
+
const options = { projection: '*', partial: 'deep' };
|
|
98
|
+
const dataType = this.dataType;
|
|
99
|
+
decoder = dataType.generateCodec('decode', options);
|
|
100
|
+
this._decoder = decoder;
|
|
101
|
+
return decoder;
|
|
24
102
|
}
|
|
25
|
-
|
|
26
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Insert a new record into database
|
|
105
|
+
*
|
|
106
|
+
* @param {PartialDTO<T>} input - The input data
|
|
107
|
+
* @param {SqbEntityService.CreateOptions} [options] - The options object
|
|
108
|
+
* @returns {Promise<PartialDTO<T>>} A promise that resolves to the created resource
|
|
109
|
+
* @throws {InternalServerError} if an unknown error occurs while creating the resource
|
|
110
|
+
* @protected
|
|
111
|
+
*/
|
|
112
|
+
async _create(input, options) {
|
|
113
|
+
const encode = this.getEncoder('create');
|
|
114
|
+
const data = encode(input);
|
|
115
|
+
const out = await this._dbCreate(data, options);
|
|
116
|
+
if (out)
|
|
117
|
+
return out;
|
|
118
|
+
throw new InternalServerError(`Unknown error while creating document for "${this.getResourceName()}"`);
|
|
27
119
|
}
|
|
28
|
-
|
|
29
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Returns the count of records based on the provided options
|
|
122
|
+
*
|
|
123
|
+
* @param {SqbEntityService.CountOptions} options - The options for the count operation.
|
|
124
|
+
* @return {Promise<number>} - A promise that resolves to the count of records
|
|
125
|
+
* @protected
|
|
126
|
+
*/
|
|
127
|
+
async _count(options) {
|
|
128
|
+
return this._dbCount(options);
|
|
30
129
|
}
|
|
31
|
-
|
|
32
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Deletes a record from the collection.
|
|
132
|
+
*
|
|
133
|
+
* @param {SQBAdapter.IdOrIds} id - The ID of the document to delete.
|
|
134
|
+
* @param {SqbEntityService.DeleteOptions} [options] - Optional delete options.
|
|
135
|
+
* @return {Promise<number>} - A Promise that resolves to the number of documents deleted.
|
|
136
|
+
* @protected
|
|
137
|
+
*/
|
|
138
|
+
async _delete(id, options) {
|
|
139
|
+
return this._dbDelete(id, options);
|
|
33
140
|
}
|
|
34
|
-
|
|
35
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Deletes multiple documents from the collection that meet the specified filter criteria.
|
|
143
|
+
*
|
|
144
|
+
* @param {SqbEntityService.DeleteManyOptions} options - The options for the delete operation.
|
|
145
|
+
* @return {Promise<number>} - A promise that resolves to the number of documents deleted.
|
|
146
|
+
* @protected
|
|
147
|
+
*/
|
|
148
|
+
async _deleteMany(options) {
|
|
149
|
+
return await this._dbDeleteMany(options);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Checks if a record with the given id exists.
|
|
153
|
+
*
|
|
154
|
+
* @param {SQBAdapter.IdOrIds} id - The id of the object to check.
|
|
155
|
+
* @param {SqbEntityService.ExistsOptions} [options] - The options for the query (optional).
|
|
156
|
+
* @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
|
|
157
|
+
* @protected
|
|
158
|
+
*/
|
|
159
|
+
async _exists(id, options) {
|
|
160
|
+
return await this._dbExists(id, options);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Checks if a record with the given arguments exists.
|
|
164
|
+
*
|
|
165
|
+
* @param {SqbEntityService.ExistsOneOptions} [options] - The options for the query (optional).
|
|
166
|
+
* @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
|
|
167
|
+
* @protected
|
|
168
|
+
*/
|
|
169
|
+
async _existsOne(options) {
|
|
170
|
+
return await this._dbExistsOne(options);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Finds a record by ID.
|
|
174
|
+
*
|
|
175
|
+
* @param {SQBAdapter.Id} id - The ID of the record.
|
|
176
|
+
* @param {SqbEntityService.FindOneOptions} [options] - The options for the find query.
|
|
177
|
+
* @return {Promise<PartialDTO<T | undefined>>} - A promise resolving to the found document, or undefined if not found.
|
|
178
|
+
* @protected
|
|
179
|
+
*/
|
|
180
|
+
async _findById(id, options) {
|
|
181
|
+
const decode = this.getDecoder();
|
|
182
|
+
const out = await this._dbFindById(id, options);
|
|
183
|
+
return out ? decode(out) : undefined;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Finds a record in the collection that matches the specified options.
|
|
187
|
+
*
|
|
188
|
+
* @param {SqbEntityService.FindOneOptions} [options] - The options for the query.
|
|
189
|
+
* @return {Promise<PartialDTO<T> | undefined>} A promise that resolves with the found document or undefined if no document is found.
|
|
190
|
+
* @protected
|
|
191
|
+
*/
|
|
192
|
+
async _findOne(options) {
|
|
193
|
+
const decode = this.getDecoder();
|
|
194
|
+
const out = await this._dbFindOne(options);
|
|
195
|
+
return out ? decode(out) : undefined;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Finds multiple records in collection.
|
|
199
|
+
*
|
|
200
|
+
* @param {SqbEntityService.FindManyOptions} [options] - The options for the find operation.
|
|
201
|
+
* @return A Promise that resolves to an array of partial outputs of type T.
|
|
202
|
+
* @protected
|
|
203
|
+
*/
|
|
204
|
+
async _findMany(options) {
|
|
205
|
+
const decode = this.getDecoder();
|
|
206
|
+
const out = await this._dbFindMany(options);
|
|
207
|
+
if (out?.length) {
|
|
208
|
+
return out.map(x => decode(x));
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Updates a record with the given id in the collection.
|
|
214
|
+
*
|
|
215
|
+
* @param {SQBAdapter.IdOrIds} id - The id of the document to update.
|
|
216
|
+
* @param {PatchDTO<T>} input - The partial input object containing the fields to update.
|
|
217
|
+
* @param {SqbEntityService.UpdateOptions} [options] - The options for the update operation.
|
|
218
|
+
* @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the updated document or
|
|
219
|
+
* undefined if the document was not found.
|
|
220
|
+
* @protected
|
|
221
|
+
*/
|
|
222
|
+
async _update(id, input, options) {
|
|
223
|
+
const encode = this.getEncoder('update');
|
|
224
|
+
const decode = this.getDecoder();
|
|
225
|
+
const data = encode(input);
|
|
226
|
+
const out = await this._dbUpdate(id, data, options);
|
|
227
|
+
if (out)
|
|
228
|
+
return decode(out);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Updates a record in the collection with the specified ID and returns updated record count
|
|
232
|
+
*
|
|
233
|
+
* @param {any} id - The ID of the document to update.
|
|
234
|
+
* @param {PatchDTO<T>} input - The partial input data to update the document with.
|
|
235
|
+
* @param {SqbEntityService.UpdateOptions} options - The options for updating the document.
|
|
236
|
+
* @returns {Promise<number>} - A promise that resolves to the number of documents modified.
|
|
237
|
+
* @protected
|
|
238
|
+
*/
|
|
239
|
+
async _updateOnly(id, input, options) {
|
|
240
|
+
const encode = this.getEncoder('create');
|
|
241
|
+
const data = encode(input);
|
|
242
|
+
return await this._dbUpdateOnly(id, data, options);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Updates multiple records in the collection based on the specified input and options.
|
|
246
|
+
*
|
|
247
|
+
* @param {PatchDTO<T>} input - The partial input to update the documents with.
|
|
248
|
+
* @param {SqbEntityService.UpdateManyOptions} options - The options for updating the documents.
|
|
249
|
+
* @return {Promise<number>} - A promise that resolves to the number of documents matched and modified.
|
|
250
|
+
* @protected
|
|
251
|
+
*/
|
|
252
|
+
async _updateMany(input, options) {
|
|
253
|
+
const encode = this.getEncoder('update');
|
|
254
|
+
const data = encode(input);
|
|
255
|
+
return await this._dbUpdateMany(data, options);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Acquires a connection and performs Repository.create operation
|
|
259
|
+
*
|
|
260
|
+
* @param input - The document to insert
|
|
261
|
+
* @param options - Optional settings for the command
|
|
262
|
+
* @protected
|
|
263
|
+
*/
|
|
264
|
+
async _dbCreate(input, options) {
|
|
265
|
+
const conn = await this.getConnection();
|
|
266
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
267
|
+
return await repo.create(input, options);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Acquires a connection and performs Repository.count operation
|
|
271
|
+
*
|
|
272
|
+
* @param options - The options for counting documents.
|
|
273
|
+
* @protected
|
|
274
|
+
*/
|
|
275
|
+
async _dbCount(options) {
|
|
276
|
+
const conn = await this.getConnection();
|
|
277
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
278
|
+
if (options?.filter)
|
|
279
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
280
|
+
return await repo.count(options);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Acquires a connection and performs Repository.delete operation
|
|
284
|
+
*
|
|
285
|
+
* @param id - Value of the key field used to select the record
|
|
286
|
+
* @param options - Optional settings for the command
|
|
287
|
+
* @protected
|
|
288
|
+
*/
|
|
289
|
+
async _dbDelete(id, options) {
|
|
290
|
+
const conn = await this.getConnection();
|
|
291
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
292
|
+
if (options?.filter)
|
|
293
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
294
|
+
return (await repo.delete(id, options)) ? 1 : 0;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Acquires a connection and performs Repository.deleteMany operation
|
|
298
|
+
*
|
|
299
|
+
* @param options - Optional settings for the command
|
|
300
|
+
* @protected
|
|
301
|
+
*/
|
|
302
|
+
async _dbDeleteMany(options) {
|
|
303
|
+
const conn = await this.getConnection();
|
|
304
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
305
|
+
if (options?.filter)
|
|
306
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
307
|
+
return await repo.deleteMany(options);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Acquires a connection and performs Repository.exists operation
|
|
311
|
+
*
|
|
312
|
+
* @param id - Value of the key field used to select the record
|
|
313
|
+
* @param options - Optional settings for the command
|
|
314
|
+
* @protected
|
|
315
|
+
*/
|
|
316
|
+
async _dbExists(id, options) {
|
|
317
|
+
const conn = await this.getConnection();
|
|
318
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
319
|
+
if (options?.filter)
|
|
320
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
321
|
+
return await repo.exists(id, options);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Acquires a connection and performs Repository.existsOne operation
|
|
325
|
+
*
|
|
326
|
+
* @param options - Optional settings for the command
|
|
327
|
+
* @protected
|
|
328
|
+
*/
|
|
329
|
+
async _dbExistsOne(options) {
|
|
330
|
+
const conn = await this.getConnection();
|
|
331
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
332
|
+
if (options?.filter)
|
|
333
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
334
|
+
return await repo.existsOne(options);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Acquires a connection and performs Repository.findById operation
|
|
338
|
+
*
|
|
339
|
+
* @param id - Value of the key field used to select the record
|
|
340
|
+
* @param options - Optional settings for the command
|
|
341
|
+
* @protected
|
|
342
|
+
*/
|
|
343
|
+
async _dbFindById(id, options) {
|
|
344
|
+
const conn = await this.getConnection();
|
|
345
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
346
|
+
if (options?.filter)
|
|
347
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
348
|
+
return await repo.findById(id, options);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Acquires a connection and performs Repository.findOne operation
|
|
352
|
+
*
|
|
353
|
+
* @param options - Optional settings for the command
|
|
354
|
+
* @protected
|
|
355
|
+
*/
|
|
356
|
+
async _dbFindOne(options) {
|
|
357
|
+
const conn = await this.getConnection();
|
|
358
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
359
|
+
if (options?.filter)
|
|
360
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
361
|
+
return await repo.findOne(options);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Acquires a connection and performs Repository.findMany operation
|
|
365
|
+
*
|
|
366
|
+
* @param options - Optional settings for the command
|
|
367
|
+
* @protected
|
|
368
|
+
*/
|
|
369
|
+
async _dbFindMany(options) {
|
|
370
|
+
const conn = await this.getConnection();
|
|
371
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
372
|
+
if (options?.filter)
|
|
373
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
374
|
+
return await repo.findMany(options);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Acquires a connection and performs Repository.update operation
|
|
378
|
+
*
|
|
379
|
+
* @param id - Value of the key field used to select the record
|
|
380
|
+
* @param data - The update values to be applied to the document
|
|
381
|
+
* @param options - Optional settings for the command
|
|
382
|
+
* @protected
|
|
383
|
+
*/
|
|
384
|
+
async _dbUpdate(id, data, options) {
|
|
385
|
+
const conn = await this.getConnection();
|
|
386
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
387
|
+
if (options?.filter)
|
|
388
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
389
|
+
return await repo.update(id, data, options);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Acquires a connection and performs Repository.updateOnly operation
|
|
393
|
+
*
|
|
394
|
+
* @param id - Value of the key field used to select the record
|
|
395
|
+
* @param data - The update values to be applied to the document
|
|
396
|
+
* @param options - Optional settings for the command
|
|
397
|
+
* @protected
|
|
398
|
+
*/
|
|
399
|
+
async _dbUpdateOnly(id, data, options) {
|
|
400
|
+
const conn = await this.getConnection();
|
|
401
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
402
|
+
if (options?.filter)
|
|
403
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
404
|
+
return await repo.updateOnly(id, data, options);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Acquires a connection and performs Repository.updateMany operation
|
|
408
|
+
*
|
|
409
|
+
* @param data - The update values to be applied to the document
|
|
410
|
+
* @param options - Optional settings for the command
|
|
411
|
+
* @protected
|
|
412
|
+
*/
|
|
413
|
+
async _dbUpdateMany(data, options) {
|
|
414
|
+
const conn = await this.getConnection();
|
|
415
|
+
const repo = conn.getRepository(this.dataTypeClass);
|
|
416
|
+
if (options?.filter)
|
|
417
|
+
options.filter = SQBAdapter.parseFilter(options.filter);
|
|
418
|
+
return await repo.updateMany(data, options);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Retrieves the database connection.
|
|
422
|
+
*
|
|
423
|
+
* @protected
|
|
424
|
+
*
|
|
425
|
+
* @throws {Error} If the context or database is not set.
|
|
426
|
+
*/
|
|
427
|
+
getConnection() {
|
|
428
|
+
const db = typeof this.db === 'function' ? this.db(this) : this.db;
|
|
429
|
+
if (!db)
|
|
430
|
+
throw new Error(`Database not set!`);
|
|
431
|
+
return db;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Retrieves the common filter used for querying documents.
|
|
435
|
+
* This method is mostly used for security issues like securing multi-tenant applications.
|
|
436
|
+
*
|
|
437
|
+
* @protected
|
|
438
|
+
* @returns {FilterInput | Promise<FilterInput> | undefined} The common filter or a Promise
|
|
439
|
+
* that resolves to the common filter, or undefined if not available.
|
|
440
|
+
*/
|
|
441
|
+
_getCommonFilter(args) {
|
|
442
|
+
return typeof this.$commonFilter === 'function' ? this.$commonFilter(args, this) : this.$commonFilter;
|
|
443
|
+
}
|
|
444
|
+
async _intercept(callback, args) {
|
|
445
|
+
try {
|
|
446
|
+
if (this.$interceptor)
|
|
447
|
+
return this.$interceptor(callback, args, this);
|
|
448
|
+
return callback();
|
|
449
|
+
}
|
|
450
|
+
catch (e) {
|
|
451
|
+
Error.captureStackTrace(e, this._intercept);
|
|
452
|
+
await this.$onError?.(e, this);
|
|
453
|
+
throw e;
|
|
454
|
+
}
|
|
36
455
|
}
|
|
37
456
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { ResourceNotAvailableError } from '@opra/common';
|
|
2
|
+
import { EntityMetadata } from '@sqb/connect';
|
|
3
|
+
import { SQBAdapter } from './sqb-adapter.js';
|
|
4
|
+
import { SqbEntityService } from './sqb-entity-service.js';
|
|
5
|
+
/**
|
|
6
|
+
* @class SqbSingletonService
|
|
7
|
+
* @template T - The data type class type of the resource
|
|
8
|
+
*/
|
|
9
|
+
export class SqbSingletonService extends SqbEntityService {
|
|
10
|
+
/**
|
|
11
|
+
* Constructs a new instance
|
|
12
|
+
*
|
|
13
|
+
* @param {Type | string} dataType - The data type of the array elements.
|
|
14
|
+
* @param {SqbSingletonService.Options} [options] - The options for the array service.
|
|
15
|
+
* @constructor
|
|
16
|
+
*/
|
|
17
|
+
constructor(dataType, options) {
|
|
18
|
+
super(dataType, options);
|
|
19
|
+
this.id = this.id || options?.id || 1;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Asserts the existence of a resource based on the given options.
|
|
23
|
+
*
|
|
24
|
+
* @returns {Promise<void>} A Promise that resolves when the resource exists.
|
|
25
|
+
* @throws {ResourceNotAvailableError} If the resource does not exist.
|
|
26
|
+
*/
|
|
27
|
+
async assert(options) {
|
|
28
|
+
if (!(await this.exists(options)))
|
|
29
|
+
throw new ResourceNotAvailableError(this.getResourceName());
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Inserts a single record into the database.
|
|
33
|
+
*
|
|
34
|
+
* @param {PartialDTO<T>} input - The input data
|
|
35
|
+
* @param {SqbSingletonService.CreateOptions} [options] - The options object
|
|
36
|
+
* @returns {Promise<PartialDTO<T>>} A promise that resolves to the created resource
|
|
37
|
+
* @throws {Error} if an unknown error occurs while creating the resource
|
|
38
|
+
*/
|
|
39
|
+
async create(input, options) {
|
|
40
|
+
const info = {
|
|
41
|
+
crud: 'create',
|
|
42
|
+
method: 'create',
|
|
43
|
+
byId: false,
|
|
44
|
+
input,
|
|
45
|
+
options,
|
|
46
|
+
};
|
|
47
|
+
return this._intercept(async () => {
|
|
48
|
+
const primaryFields = EntityMetadata.getPrimaryIndexColumns(this.entityMetadata);
|
|
49
|
+
const data = { ...input };
|
|
50
|
+
if (primaryFields.length > 1) {
|
|
51
|
+
if (typeof primaryFields !== 'object')
|
|
52
|
+
throw new TypeError(`"${this.entityMetadata.name}" should has multiple primary key fields. So you should provide and object that contains key fields`);
|
|
53
|
+
for (const field of primaryFields) {
|
|
54
|
+
data[field.name] = this.id[field.name];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
data[primaryFields[0].name] = this.id;
|
|
59
|
+
return await this._create(data, options);
|
|
60
|
+
}, info);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Deletes the singleton record
|
|
64
|
+
*
|
|
65
|
+
* @param {SqbSingletonService.DeleteOptions} [options] - The options object
|
|
66
|
+
* @return {Promise<number>} - A Promise that resolves to the number of records deleted
|
|
67
|
+
*/
|
|
68
|
+
async delete(options) {
|
|
69
|
+
const info = {
|
|
70
|
+
crud: 'delete',
|
|
71
|
+
method: 'delete',
|
|
72
|
+
byId: true,
|
|
73
|
+
documentId: this.id,
|
|
74
|
+
options,
|
|
75
|
+
};
|
|
76
|
+
return this._intercept(async () => {
|
|
77
|
+
const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
|
|
78
|
+
return this._delete(this.id, { ...options, filter });
|
|
79
|
+
}, info);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks if the singleton record exists.
|
|
83
|
+
*
|
|
84
|
+
* @param {SqbSingletonService.ExistsOptions} [options] - The options for the query (optional).
|
|
85
|
+
* @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
|
|
86
|
+
*/
|
|
87
|
+
async exists(options) {
|
|
88
|
+
const info = {
|
|
89
|
+
crud: 'read',
|
|
90
|
+
method: 'exists',
|
|
91
|
+
byId: true,
|
|
92
|
+
documentId: this.id,
|
|
93
|
+
options,
|
|
94
|
+
};
|
|
95
|
+
return this._intercept(async () => {
|
|
96
|
+
const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
|
|
97
|
+
return this._exists(this.id, { ...options, filter });
|
|
98
|
+
}, info);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Finds the singleton record. Returns `undefined` if not found
|
|
102
|
+
*
|
|
103
|
+
* @param {SqbSingletonService.FindOneOptions} options - The options for the query.
|
|
104
|
+
* @return {Promise<PartialDTO<T> | undefined>} A promise that resolves with the found document or undefined if no document is found.
|
|
105
|
+
*/
|
|
106
|
+
async find(options) {
|
|
107
|
+
const info = {
|
|
108
|
+
crud: 'read',
|
|
109
|
+
method: 'findById',
|
|
110
|
+
byId: true,
|
|
111
|
+
documentId: this.id,
|
|
112
|
+
options,
|
|
113
|
+
};
|
|
114
|
+
return this._intercept(async () => {
|
|
115
|
+
const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
|
|
116
|
+
return this._findById(this.id, { ...options, filter });
|
|
117
|
+
}, info);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Retrieves the singleton record. Throws error if not found.
|
|
121
|
+
*
|
|
122
|
+
* @param {SqbSingletonService.FindOptions} [options] - Optional options for the `find` operation.
|
|
123
|
+
* @returns {Promise<PartialDTO<T>>} - A promise that resolves to the retrieved document,
|
|
124
|
+
* or rejects with a ResourceNotFoundError if the document does not exist.
|
|
125
|
+
* @throws {ResourceNotAvailableError} - If the document does not exist.
|
|
126
|
+
*/
|
|
127
|
+
async get(options) {
|
|
128
|
+
const out = await this.find(options);
|
|
129
|
+
if (!out)
|
|
130
|
+
throw new ResourceNotAvailableError(this.getResourceName());
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Updates the singleton.
|
|
135
|
+
*
|
|
136
|
+
* @param {PatchDTO<T>} input - The partial input object containing the fields to update.
|
|
137
|
+
* @param {SqbSingletonService.UpdateOptions} [options] - The options for the update operation.
|
|
138
|
+
* @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the updated document or
|
|
139
|
+
* undefined if the document was not found.
|
|
140
|
+
*/
|
|
141
|
+
async update(input, options) {
|
|
142
|
+
const info = {
|
|
143
|
+
crud: 'update',
|
|
144
|
+
method: 'update',
|
|
145
|
+
documentId: this.id,
|
|
146
|
+
byId: true,
|
|
147
|
+
input,
|
|
148
|
+
options,
|
|
149
|
+
};
|
|
150
|
+
return this._intercept(async () => {
|
|
151
|
+
const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
|
|
152
|
+
return this._update(this.id, input, { ...options, filter });
|
|
153
|
+
}, info);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Updates the singleton and returns updated record count
|
|
157
|
+
*
|
|
158
|
+
* @param {PatchDTO<T>} input - The partial input data to update the document with.
|
|
159
|
+
* @param {SqbSingletonService.UpdateOptions} options - The options for updating the document.
|
|
160
|
+
* @returns {Promise<number>} - A promise that resolves to the number of documents modified.
|
|
161
|
+
*/
|
|
162
|
+
async updateOnly(input, options) {
|
|
163
|
+
const info = {
|
|
164
|
+
crud: 'update',
|
|
165
|
+
method: 'update',
|
|
166
|
+
documentId: this.id,
|
|
167
|
+
byId: true,
|
|
168
|
+
input,
|
|
169
|
+
options,
|
|
170
|
+
};
|
|
171
|
+
return this._intercept(async () => {
|
|
172
|
+
const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
|
|
173
|
+
return this._updateOnly(this.id, input, { ...options, filter });
|
|
174
|
+
}, info);
|
|
175
|
+
}
|
|
176
|
+
}
|