@opra/sqb 1.0.0-alpha.16 → 1.0.0-alpha.18

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.
@@ -1,6 +1,7 @@
1
1
  import { InternalServerError } from '@opra/common';
2
2
  import { ServiceBase } from '@opra/core';
3
3
  import { EntityMetadata } from '@sqb/connect';
4
+ import { isNotNullish } from 'valgen';
4
5
  import { SQBAdapter } from './sqb-adapter.js';
5
6
  /**
6
7
  * @class SqbEntityService
@@ -20,9 +21,9 @@ export class SqbEntityService extends ServiceBase {
20
21
  this._outputCodecs = {};
21
22
  this._dataType_ = dataType;
22
23
  this.db = options?.db;
23
- this.$resourceName = options?.resourceName;
24
- this.$commonFilter = this.$commonFilter || options?.commonFilter;
25
- this.$interceptor = this.$interceptor || options?.interceptor;
24
+ this.resourceName = options?.resourceName;
25
+ this.commonFilter = options?.commonFilter;
26
+ this.interceptor = options?.interceptor;
26
27
  }
27
28
  /**
28
29
  * Retrieves the OPRA data type
@@ -66,7 +67,7 @@ export class SqbEntityService extends ServiceBase {
66
67
  * @throws {Error} If the collection name is not defined.
67
68
  */
68
69
  getResourceName() {
69
- const out = typeof this.$resourceName === 'function' ? this.$resourceName(this) : this.$resourceName || this.dataType.name;
70
+ const out = typeof this.resourceName === 'function' ? this.resourceName(this) : this.resourceName || this.dataType.name;
70
71
  if (out)
71
72
  return out;
72
73
  throw new Error('resourceName is not defined');
@@ -104,13 +105,13 @@ export class SqbEntityService extends ServiceBase {
104
105
  /**
105
106
  * Insert a new record into database
106
107
  *
107
- * @param {PartialDTO<T>} input - The input data
108
- * @param {SqbEntityService.CreateOptions} [options] - The options object
109
- * @returns {Promise<PartialDTO<T>>} A promise that resolves to the created resource
110
- * @throws {InternalServerError} if an unknown error occurs while creating the resource
108
+ * @param command
109
+ * @returns - A promise that resolves to the created resource
111
110
  * @protected
112
111
  */
113
- async _create(input, options) {
112
+ async _create(command) {
113
+ const { input, options } = command;
114
+ isNotNullish(command.input, { label: 'input' });
114
115
  const inputCodec = this.getInputCodec('create');
115
116
  const outputCodec = this.getOutputCodec('create');
116
117
  const data = inputCodec(input);
@@ -122,90 +123,89 @@ export class SqbEntityService extends ServiceBase {
122
123
  /**
123
124
  * Returns the count of records based on the provided options
124
125
  *
125
- * @param {SqbEntityService.CountOptions} options - The options for the count operation.
126
- * @return {Promise<number>} - A promise that resolves to the count of records
126
+ * @param command
127
+ * @return - A promise that resolves to the count of records
127
128
  * @protected
128
129
  */
129
- async _count(options) {
130
- return this._dbCount(options);
130
+ async _count(command) {
131
+ return this._dbCount(command.options);
131
132
  }
132
133
  /**
133
134
  * Deletes a record from the collection.
134
135
  *
135
- * @param {SQBAdapter.IdOrIds} id - The ID of the document to delete.
136
- * @param {SqbEntityService.DeleteOptions} [options] - Optional delete options.
137
- * @return {Promise<number>} - A Promise that resolves to the number of documents deleted.
136
+ * @param command
137
+ * @return - A Promise that resolves to the number of documents deleted.
138
138
  * @protected
139
139
  */
140
- async _delete(id, options) {
141
- return this._dbDelete(id, options);
140
+ async _delete(command) {
141
+ isNotNullish(command.documentId, { label: 'documentId' });
142
+ return this._dbDelete(command.documentId, command.options);
142
143
  }
143
144
  /**
144
145
  * Deletes multiple documents from the collection that meet the specified filter criteria.
145
146
  *
146
- * @param {SqbEntityService.DeleteManyOptions} options - The options for the delete operation.
147
- * @return {Promise<number>} - A promise that resolves to the number of documents deleted.
147
+ * @param command
148
+ * @return - A promise that resolves to the number of documents deleted.
148
149
  * @protected
149
150
  */
150
- async _deleteMany(options) {
151
- return await this._dbDeleteMany(options);
151
+ async _deleteMany(command) {
152
+ return await this._dbDeleteMany(command.options);
152
153
  }
153
154
  /**
154
155
  * Checks if a record with the given id exists.
155
156
  *
156
- * @param {SQBAdapter.IdOrIds} id - The id of the object to check.
157
- * @param {SqbEntityService.ExistsOptions} [options] - The options for the query (optional).
158
- * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
157
+ * @param command
159
158
  * @protected
160
159
  */
161
- async _exists(id, options) {
162
- return await this._dbExists(id, options);
160
+ async _exists(command) {
161
+ isNotNullish(command.documentId, { label: 'documentId' });
162
+ return await this._dbExists(command.documentId, command.options);
163
163
  }
164
164
  /**
165
165
  * Checks if a record with the given arguments exists.
166
166
  *
167
- * @param {SqbEntityService.ExistsOneOptions} [options] - The options for the query (optional).
168
- * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
167
+ * @param command
168
+ * @return - A Promise that resolves to a boolean indicating whether the record exists or not.
169
169
  * @protected
170
170
  */
171
- async _existsOne(options) {
172
- return await this._dbExistsOne(options);
171
+ async _existsOne(command) {
172
+ return await this._dbExistsOne(command.options);
173
173
  }
174
174
  /**
175
175
  * Finds a record by ID.
176
176
  *
177
- * @param {SQBAdapter.Id} id - The ID of the record.
178
- * @param {SqbEntityService.FindOneOptions} [options] - The options for the find query.
179
- * @return {Promise<PartialDTO<T | undefined>>} - A promise resolving to the found document, or undefined if not found.
177
+ * @param command
178
+ * @return - A promise resolving to the found document, or undefined if not found.
180
179
  * @protected
181
180
  */
182
- async _findById(id, options) {
181
+ async _findById(command) {
182
+ isNotNullish(command.documentId, { label: 'documentId' });
183
183
  const decode = this.getOutputCodec('find');
184
- const out = await this._dbFindById(id, options);
184
+ const out = await this._dbFindById(command.documentId, command.options);
185
185
  return out ? decode(out) : undefined;
186
186
  }
187
187
  /**
188
188
  * Finds a record in the collection that matches the specified options.
189
189
  *
190
- * @param {SqbEntityService.FindOneOptions} [options] - The options for the query.
191
- * @return {Promise<PartialDTO<T> | undefined>} A promise that resolves with the found document or undefined if no document is found.
190
+ * @param command
191
+ * @return - A promise that resolves with the found document or undefined if no document is found.
192
192
  * @protected
193
193
  */
194
- async _findOne(options) {
194
+ async _findOne(command) {
195
195
  const decode = this.getOutputCodec('find');
196
- const out = await this._dbFindOne(options);
196
+ const out = await this._dbFindOne(command.options);
197
197
  return out ? decode(out) : undefined;
198
198
  }
199
199
  /**
200
200
  * Finds multiple records in collection.
201
201
  *
202
- * @param {SqbEntityService.FindManyOptions} [options] - The options for the find operation.
203
- * @return A Promise that resolves to an array of partial outputs of type T.
202
+ * @param command
203
+ * @return - A Promise that resolves to an array of partial outputs of type T.
204
204
  * @protected
205
205
  */
206
- async _findMany(options) {
206
+ async _findMany(command) {
207
207
  const decode = this.getOutputCodec('find');
208
- const out = await this._dbFindMany(options);
208
+ const out = await this._dbFindMany(command.options);
209
209
  if (out?.length) {
210
210
  return out.map(x => decode(x));
211
211
  }
@@ -214,17 +214,17 @@ export class SqbEntityService extends ServiceBase {
214
214
  /**
215
215
  * Updates a record with the given id in the collection.
216
216
  *
217
- * @param {SQBAdapter.IdOrIds} id - The id of the document to update.
218
- * @param {PatchDTO<T>} input - The partial input object containing the fields to update.
219
- * @param {SqbEntityService.UpdateOptions} [options] - The options for the update operation.
220
- * @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the updated document or
221
- * undefined if the document was not found.
217
+ * @param command
218
+ * @returns A promise that resolves to the updated document or undefined if the document was not found.
222
219
  * @protected
223
220
  */
224
- async _update(id, input, options) {
221
+ async _update(command) {
222
+ isNotNullish(command.documentId, { label: 'documentId' });
223
+ isNotNullish(command.input, { label: 'input' });
224
+ const { documentId, input, options } = command;
225
225
  const inputCodec = this.getInputCodec('update');
226
226
  const data = inputCodec(input);
227
- const out = await this._dbUpdate(id, data, options);
227
+ const out = await this._dbUpdate(documentId, data, options);
228
228
  const outputCodec = this.getOutputCodec('update');
229
229
  if (out)
230
230
  return outputCodec(out);
@@ -232,29 +232,30 @@ export class SqbEntityService extends ServiceBase {
232
232
  /**
233
233
  * Updates a record in the collection with the specified ID and returns updated record count
234
234
  *
235
- * @param {any} id - The ID of the document to update.
236
- * @param {PatchDTO<T>} input - The partial input data to update the document with.
237
- * @param {SqbEntityService.UpdateOptions} options - The options for updating the document.
238
- * @returns {Promise<number>} - A promise that resolves to the number of documents modified.
235
+ * @param command
236
+ * @returns - A promise that resolves to the number of documents modified.
239
237
  * @protected
240
238
  */
241
- async _updateOnly(id, input, options) {
239
+ async _updateOnly(command) {
240
+ isNotNullish(command.documentId, { label: 'documentId' });
241
+ isNotNullish(command.input, { label: 'input' });
242
+ const { documentId, input, options } = command;
242
243
  const inputCodec = this.getInputCodec('update');
243
244
  const data = inputCodec(input);
244
- return await this._dbUpdateOnly(id, data, options);
245
+ return await this._dbUpdateOnly(documentId, data, options);
245
246
  }
246
247
  /**
247
248
  * Updates multiple records in the collection based on the specified input and options.
248
249
  *
249
- * @param {PatchDTO<T>} input - The partial input to update the documents with.
250
- * @param {SqbEntityService.UpdateManyOptions} options - The options for updating the documents.
251
- * @return {Promise<number>} - A promise that resolves to the number of documents matched and modified.
250
+ * @param command
251
+ * @return - A promise that resolves to the number of documents matched and modified.
252
252
  * @protected
253
253
  */
254
- async _updateMany(input, options) {
254
+ async _updateMany(command) {
255
+ isNotNullish(command.input, { label: 'input' });
255
256
  const inputCodec = this.getInputCodec('update');
256
- const data = inputCodec(input);
257
- return await this._dbUpdateMany(data, options);
257
+ const data = inputCodec(command.input);
258
+ return await this._dbUpdateMany(data, command.options);
258
259
  }
259
260
  /**
260
261
  * Acquires a connection and performs Repository.create operation
@@ -441,17 +442,28 @@ export class SqbEntityService extends ServiceBase {
441
442
  * that resolves to the common filter, or undefined if not available.
442
443
  */
443
444
  _getCommonFilter(args) {
444
- return typeof this.$commonFilter === 'function' ? this.$commonFilter(args, this) : this.$commonFilter;
445
- }
446
- async _intercept(callback, args) {
445
+ return typeof this.commonFilter === 'function' ? this.commonFilter(args, this) : this.commonFilter;
446
+ }
447
+ async _executeCommand(command, commandFn) {
448
+ let proto;
449
+ const next = async () => {
450
+ proto = proto ? Object.getPrototypeOf(proto) : this;
451
+ while (proto) {
452
+ if (proto.interceptor) {
453
+ return await proto.interceptor.call(this, next, command, this);
454
+ }
455
+ proto = Object.getPrototypeOf(proto);
456
+ if (!(proto instanceof SqbEntityService))
457
+ break;
458
+ }
459
+ return commandFn();
460
+ };
447
461
  try {
448
- if (this.$interceptor)
449
- return this.$interceptor(callback, args, this);
450
- return callback();
462
+ return await next();
451
463
  }
452
464
  catch (e) {
453
- Error.captureStackTrace(e, this._intercept);
454
- await this.$onError?.(e, this);
465
+ Error.captureStackTrace(e, this._executeCommand);
466
+ await this.onError?.(e, this);
455
467
  throw e;
456
468
  }
457
469
  }
@@ -37,16 +37,16 @@ export class SqbSingletonService extends SqbEntityService {
37
37
  * @throws {Error} if an unknown error occurs while creating the resource
38
38
  */
39
39
  async create(input, options) {
40
- const info = {
40
+ const command = {
41
41
  crud: 'create',
42
42
  method: 'create',
43
43
  byId: false,
44
44
  input,
45
45
  options,
46
46
  };
47
- return this._intercept(async () => {
47
+ return this._executeCommand(command, async () => {
48
48
  const primaryFields = EntityMetadata.getPrimaryIndexColumns(this.entityMetadata);
49
- const data = { ...input };
49
+ const data = { ...command.input };
50
50
  if (primaryFields.length > 1) {
51
51
  if (typeof primaryFields !== 'object') {
52
52
  throw new TypeError(`"${this.entityMetadata.name}" should has multiple primary key fields. So you should provide and object that contains key fields`);
@@ -57,8 +57,9 @@ export class SqbSingletonService extends SqbEntityService {
57
57
  }
58
58
  else
59
59
  data[primaryFields[0].name] = this.id;
60
- return await this._create(data, options);
61
- }, info);
60
+ command.input = data;
61
+ return await this._create(command);
62
+ });
62
63
  }
63
64
  /**
64
65
  * Deletes the singleton record
@@ -67,17 +68,18 @@ export class SqbSingletonService extends SqbEntityService {
67
68
  * @return {Promise<number>} - A Promise that resolves to the number of records deleted
68
69
  */
69
70
  async delete(options) {
70
- const info = {
71
+ const command = {
71
72
  crud: 'delete',
72
73
  method: 'delete',
73
74
  byId: true,
74
75
  documentId: this.id,
75
76
  options,
76
77
  };
77
- return this._intercept(async () => {
78
- const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
79
- return this._delete(this.id, { ...options, filter });
80
- }, info);
78
+ return this._executeCommand(command, async () => {
79
+ const filter = SQBAdapter.parseFilter([await this._getCommonFilter(command), command.options?.filter]);
80
+ command.options = { ...command.options, filter };
81
+ return this._delete(command);
82
+ });
81
83
  }
82
84
  /**
83
85
  * Checks if the singleton record exists.
@@ -86,17 +88,18 @@ export class SqbSingletonService extends SqbEntityService {
86
88
  * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the record exists or not.
87
89
  */
88
90
  async exists(options) {
89
- const info = {
91
+ const command = {
90
92
  crud: 'read',
91
93
  method: 'exists',
92
94
  byId: true,
93
95
  documentId: this.id,
94
96
  options,
95
97
  };
96
- return this._intercept(async () => {
97
- const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
98
- return this._exists(this.id, { ...options, filter });
99
- }, info);
98
+ return this._executeCommand(command, async () => {
99
+ const filter = SQBAdapter.parseFilter([await this._getCommonFilter(command), command.options?.filter]);
100
+ command.options = { ...command.options, filter };
101
+ return this._exists(command);
102
+ });
100
103
  }
101
104
  /**
102
105
  * Finds the singleton record. Returns `undefined` if not found
@@ -105,17 +108,18 @@ export class SqbSingletonService extends SqbEntityService {
105
108
  * @return {Promise<PartialDTO<T> | undefined>} A promise that resolves with the found document or undefined if no document is found.
106
109
  */
107
110
  async find(options) {
108
- const info = {
111
+ const command = {
109
112
  crud: 'read',
110
113
  method: 'findById',
111
114
  byId: true,
112
115
  documentId: this.id,
113
116
  options,
114
117
  };
115
- return this._intercept(async () => {
116
- const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
117
- return this._findById(this.id, { ...options, filter });
118
- }, info);
118
+ return this._executeCommand(command, async () => {
119
+ const filter = SQBAdapter.parseFilter([await this._getCommonFilter(command), command.options?.filter]);
120
+ command.options = { ...command.options, filter };
121
+ return this._findById(command);
122
+ });
119
123
  }
120
124
  /**
121
125
  * Retrieves the singleton record. Throws error if not found.
@@ -140,7 +144,7 @@ export class SqbSingletonService extends SqbEntityService {
140
144
  * undefined if the document was not found.
141
145
  */
142
146
  async update(input, options) {
143
- const info = {
147
+ const command = {
144
148
  crud: 'update',
145
149
  method: 'update',
146
150
  documentId: this.id,
@@ -148,10 +152,11 @@ export class SqbSingletonService extends SqbEntityService {
148
152
  input,
149
153
  options,
150
154
  };
151
- return this._intercept(async () => {
152
- const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
153
- return this._update(this.id, input, { ...options, filter });
154
- }, info);
155
+ return this._executeCommand(command, async () => {
156
+ const filter = SQBAdapter.parseFilter([await this._getCommonFilter(command), command.options?.filter]);
157
+ command.options = { ...command.options, filter };
158
+ return this._update(command);
159
+ });
155
160
  }
156
161
  /**
157
162
  * Updates the singleton and returns updated record count
@@ -161,7 +166,7 @@ export class SqbSingletonService extends SqbEntityService {
161
166
  * @returns {Promise<number>} - A promise that resolves to the number of documents modified.
162
167
  */
163
168
  async updateOnly(input, options) {
164
- const info = {
169
+ const command = {
165
170
  crud: 'update',
166
171
  method: 'update',
167
172
  documentId: this.id,
@@ -169,9 +174,10 @@ export class SqbSingletonService extends SqbEntityService {
169
174
  input,
170
175
  options,
171
176
  };
172
- return this._intercept(async () => {
173
- const filter = SQBAdapter.parseFilter([await this._getCommonFilter(info), options?.filter]);
174
- return this._updateOnly(this.id, input, { ...options, filter });
175
- }, info);
177
+ return this._executeCommand(command, async () => {
178
+ const filter = SQBAdapter.parseFilter([await this._getCommonFilter(command), command.options?.filter]);
179
+ command.options = { ...command.options, filter };
180
+ return this._updateOnly(command);
181
+ });
176
182
  }
177
183
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/sqb",
3
- "version": "1.0.0-alpha.16",
3
+ "version": "1.0.0-alpha.18",
4
4
  "description": "Opra SQB adapter package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -35,11 +35,11 @@
35
35
  "@sqb/builder": "^4.12.1",
36
36
  "@sqb/connect": "^4.12.1",
37
37
  "@sqb/postgres": "^4.12.1",
38
- "postgresql-client": "^2.11.2",
38
+ "postgresql-client": "^2.12.0",
39
39
  "ts-gems": "^3.4.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "@opra/core": "^1.0.0-alpha.16",
42
+ "@opra/core": "^1.0.0-alpha.18",
43
43
  "@sqb/connect": ">= 4.10.6"
44
44
  },
45
45
  "type": "module",
@@ -7,7 +7,7 @@ import { SqbEntityService } from './sqb-entity-service.js';
7
7
  export declare namespace SqbCollectionService {
8
8
  interface Options extends SqbEntityService.Options {
9
9
  defaultLimit?: SqbCollectionService<any>['defaultLimit'];
10
- interceptor?: SqbCollectionService<any>['$interceptor'];
10
+ interceptor?: SqbCollectionService<any>['interceptor'];
11
11
  }
12
12
  /**
13
13
  * Represents options for "create" operation
@@ -70,14 +70,14 @@ export declare namespace SqbCollectionService {
70
70
  *
71
71
  * @interface
72
72
  */
73
- interface UpdateOptions extends SqbEntityService.UpdateOptions {
73
+ interface UpdateOptions extends SqbEntityService.UpdateOneOptions {
74
74
  }
75
75
  /**
76
76
  * Represents options for "updateOnly" operation
77
77
  *
78
78
  * @interface
79
79
  */
80
- interface UpdateOnlyOptions extends SqbEntityService.UpdateOptions {
80
+ interface UpdateOnlyOptions extends SqbEntityService.UpdateOneOptions {
81
81
  }
82
82
  /**
83
83
  * Represents options for "updateMany" operation
@@ -163,11 +163,11 @@ export declare abstract class SqbCollectionService<T extends object = object> ex
163
163
  /**
164
164
  * Finds a record by ID.
165
165
  *
166
- * @param {SQBAdapter.Id} id - The ID of the record.
166
+ * @param {SQBAdapter.IdOrIds} id - The ID of the record.
167
167
  * @param {SqbCollectionService.FindOneOptions} [options] - The options for the find query.
168
168
  * @return {Promise<PartialDTO<T | undefined>>} - A promise resolving to the found document, or undefined if not found.
169
169
  */
170
- findById(id: SQBAdapter.Id, options?: SqbCollectionService.FindOneOptions): Promise<PartialDTO<T> | undefined>;
170
+ findById(id: SQBAdapter.IdOrIds, options?: SqbCollectionService.FindOneOptions): Promise<PartialDTO<T> | undefined>;
171
171
  /**
172
172
  * Finds a record in the collection that matches the specified options.
173
173
  *
@@ -196,13 +196,13 @@ export declare abstract class SqbCollectionService<T extends object = object> ex
196
196
  /**
197
197
  * Retrieves a records from the collection by its ID. Throws error if not found.
198
198
  *
199
- * @param {SQBAdapter.Id} id - The ID of the document to retrieve.
199
+ * @param {SQBAdapter.IdOrIds} id - The ID of the document to retrieve.
200
200
  * @param {SqbCollectionService.FindOneOptions} [options] - Optional options for the findOne operation.
201
201
  * @returns {Promise<PartialDTO<T>>} - A promise that resolves to the retrieved document,
202
202
  * or rejects with a ResourceNotFoundError if the document does not exist.
203
203
  * @throws {ResourceNotAvailableError} - If the document with the specified ID does not exist.
204
204
  */
205
- get(id: SQBAdapter.Id, options?: SqbCollectionService.FindOneOptions): Promise<PartialDTO<T>>;
205
+ get(id: SQBAdapter.IdOrIds, options?: SqbCollectionService.FindOneOptions): Promise<PartialDTO<T>>;
206
206
  /**
207
207
  * Updates a record with the given id in the collection.
208
208
  *
@@ -221,7 +221,7 @@ export declare abstract class SqbCollectionService<T extends object = object> ex
221
221
  * @param {SqbCollectionService.UpdateOptions} options - The options for updating the document.
222
222
  * @returns {Promise<number>} - A promise that resolves to the number of documents modified.
223
223
  */
224
- updateOnly(id: SQBAdapter.Id, input: PatchDTO<T>, options?: SqbCollectionService.UpdateOnlyOptions): Promise<number>;
224
+ updateOnly(id: SQBAdapter.IdOrIds, input: PatchDTO<T>, options?: SqbCollectionService.UpdateOnlyOptions): Promise<number>;
225
225
  /**
226
226
  * Updates multiple records in the collection based on the specified input and options.
227
227
  *