@smartsoft001/crud-shell-app-services 2.75.0 → 2.80.0
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/index.js +333 -0
- package/package.json +1 -1
- package/src/lib/services/crud/crud.service.d.ts +54 -0
- package/src/lib/services/{index.ts → index.d.ts} +1 -3
- package/.eslintrc +0 -13
- package/jest.config.ts +0 -27
- package/project.json +0 -43
- package/src/lib/services/crud/crud.service.spec.ts +0 -228
- package/src/lib/services/crud/crud.service.ts +0 -285
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -11
- package/tsconfig.spec.json +0 -20
- /package/src/{index.ts → index.d.ts} +0 -0
- /package/{test-setup.ts → test-setup.d.ts} +0 -0
package/index.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// packages/crud/shell/app-services/src/lib/services/crud/crud.service.ts
|
|
14
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
15
|
+
import * as CombinedStream from "combined-stream";
|
|
16
|
+
import { Guid as Guid2 } from "guid-typescript";
|
|
17
|
+
import { Memoize } from "lodash-decorators";
|
|
18
|
+
|
|
19
|
+
// packages/shared/models/src/lib/symbols.ts
|
|
20
|
+
var SYMBOL_MODEL = Symbol.for("smartsoft:model");
|
|
21
|
+
var SYMBOL_FIELD = Symbol.for("smartsoft:field");
|
|
22
|
+
|
|
23
|
+
// packages/shared/models/src/lib/decorators/model/model.decorator.ts
|
|
24
|
+
import "reflect-metadata";
|
|
25
|
+
|
|
26
|
+
// packages/shared/models/src/lib/decorators/field/field.decorator.ts
|
|
27
|
+
import "reflect-metadata";
|
|
28
|
+
|
|
29
|
+
// packages/shared/utils/src/lib/services/password/password.service.ts
|
|
30
|
+
import * as md5_ from "md5";
|
|
31
|
+
var md5 = md5_;
|
|
32
|
+
var PasswordService = class _PasswordService {
|
|
33
|
+
/**
|
|
34
|
+
* Hash password text
|
|
35
|
+
* @param p {string} - text
|
|
36
|
+
* @return - hashed text
|
|
37
|
+
*/
|
|
38
|
+
static hash(p) {
|
|
39
|
+
return Promise.resolve(md5(p));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compare password text with hashed text
|
|
43
|
+
* @param p {string} - password text
|
|
44
|
+
* @param h {string} - hashed text
|
|
45
|
+
*/
|
|
46
|
+
static async compare(p, h) {
|
|
47
|
+
const hp = await _PasswordService.hash(p);
|
|
48
|
+
return hp === h;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// packages/shared/utils/src/lib/services/object/object.service.ts
|
|
53
|
+
import { stringify } from "flatted";
|
|
54
|
+
|
|
55
|
+
// packages/shared/utils/src/lib/services/guid/guid.service.ts
|
|
56
|
+
import { Guid } from "guid-typescript";
|
|
57
|
+
var GuidService = class {
|
|
58
|
+
/***
|
|
59
|
+
* Create guid as string
|
|
60
|
+
*/
|
|
61
|
+
static create() {
|
|
62
|
+
return Guid.raw();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// packages/shared/utils/src/lib/services/array/array.service.ts
|
|
67
|
+
import * as _ from "lodash";
|
|
68
|
+
|
|
69
|
+
// packages/shared/models/src/lib/utils.ts
|
|
70
|
+
function getModelFieldKeys(type) {
|
|
71
|
+
if (!type["__fields"])
|
|
72
|
+
return [];
|
|
73
|
+
return Object.keys(type["__fields"]);
|
|
74
|
+
}
|
|
75
|
+
function getModelFieldOptions(instance, fieldKey) {
|
|
76
|
+
return Reflect.getMetadata(SYMBOL_FIELD, instance, fieldKey);
|
|
77
|
+
}
|
|
78
|
+
function getModelFieldsWithOptions(instance) {
|
|
79
|
+
const keys = getModelFieldKeys(instance.constructor);
|
|
80
|
+
return keys.map((item) => {
|
|
81
|
+
return {
|
|
82
|
+
key: item,
|
|
83
|
+
options: getModelFieldOptions(instance, item)
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function isModel(instance) {
|
|
88
|
+
if (!instance || !instance.constructor)
|
|
89
|
+
return false;
|
|
90
|
+
return Reflect.hasMetadata(SYMBOL_MODEL, instance.constructor);
|
|
91
|
+
}
|
|
92
|
+
function getInvalidFields(instance, mode, permissions) {
|
|
93
|
+
const result = [];
|
|
94
|
+
getModelFieldsWithOptions(instance).forEach(({ key, options }) => {
|
|
95
|
+
let required = options.required;
|
|
96
|
+
if ((mode === "create" || mode === "update") && options[mode] && options[mode]?.constructor) {
|
|
97
|
+
required = options[mode].required;
|
|
98
|
+
if (required && permissions && options[mode].permissions) {
|
|
99
|
+
required = options[mode]?.permissions?.some(
|
|
100
|
+
(op) => permissions.some((p) => p === op)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (required && (instance[key] === null || instance[key] === void 0 || instance[key] === ""))
|
|
105
|
+
result.push(key);
|
|
106
|
+
});
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function castModel(instance, mode, permissions) {
|
|
110
|
+
if (!isModel(instance))
|
|
111
|
+
return;
|
|
112
|
+
const fieldsWithOptions = getModelFieldsWithOptions(instance);
|
|
113
|
+
Object.keys(instance).filter((key) => key !== "id").forEach((key) => {
|
|
114
|
+
const fieldWidthOptions = fieldsWithOptions.find((f) => f.key === key);
|
|
115
|
+
if (!fieldWidthOptions) {
|
|
116
|
+
delete instance[key];
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if ((mode === "create" || mode === "update") && (!fieldWidthOptions.options[mode] || permissions && fieldWidthOptions.options[mode].permissions && !fieldWidthOptions.options[mode]?.permissions?.some((op) => permissions.some((p) => op === p)))) {
|
|
120
|
+
delete instance[key];
|
|
121
|
+
return;
|
|
122
|
+
} else if (mode !== "create" && mode !== "update" && (!fieldWidthOptions.options.customs || !fieldWidthOptions.options.customs.some((c) => c.mode === mode))) {
|
|
123
|
+
delete instance[key];
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// packages/shared/domain-core/src/lib/errors.ts
|
|
130
|
+
var DomainValidationError = class _DomainValidationError extends Error {
|
|
131
|
+
constructor(msg) {
|
|
132
|
+
super(msg);
|
|
133
|
+
this.type = _DomainValidationError;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// packages/crud/shell/app-services/src/lib/services/crud/crud.service.ts
|
|
138
|
+
var CrudService = class {
|
|
139
|
+
constructor(permissionService, repository, attachmentRepository) {
|
|
140
|
+
this.permissionService = permissionService;
|
|
141
|
+
this.repository = repository;
|
|
142
|
+
this.attachmentRepository = attachmentRepository;
|
|
143
|
+
this._logger = new Logger(CrudService.name, { timestamp: true });
|
|
144
|
+
}
|
|
145
|
+
async create(data, user) {
|
|
146
|
+
data.id = Guid2.raw();
|
|
147
|
+
try {
|
|
148
|
+
this.permissionService.valid("create", user);
|
|
149
|
+
castModel(data, "create", user.permissions);
|
|
150
|
+
this.checkValidCreate(data, user.permissions);
|
|
151
|
+
if (data["password"]) {
|
|
152
|
+
data["password"] = await PasswordService.hash(data["password"]);
|
|
153
|
+
}
|
|
154
|
+
if (data["passwordConfirm"]) {
|
|
155
|
+
delete data["passwordConfirm"];
|
|
156
|
+
}
|
|
157
|
+
await this.repository.create(data, user);
|
|
158
|
+
return data.id;
|
|
159
|
+
} catch (e) {
|
|
160
|
+
this._logger.error(e);
|
|
161
|
+
throw e;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async createMany(data, user, options) {
|
|
165
|
+
data.forEach((item) => {
|
|
166
|
+
item.id = Guid2.raw();
|
|
167
|
+
});
|
|
168
|
+
try {
|
|
169
|
+
this.permissionService.valid("create", user);
|
|
170
|
+
data.forEach((item) => {
|
|
171
|
+
castModel(item, "create", user.permissions);
|
|
172
|
+
this.checkValidCreate(item, user.permissions);
|
|
173
|
+
});
|
|
174
|
+
if (options && options.mode === "replace") {
|
|
175
|
+
await this.repository.clear(user);
|
|
176
|
+
}
|
|
177
|
+
for (let index = 0; index < data.length; index++) {
|
|
178
|
+
const item = data[index];
|
|
179
|
+
if (item["password"]) {
|
|
180
|
+
item["password"] = await PasswordService.hash(item["password"]);
|
|
181
|
+
}
|
|
182
|
+
if (item["passwordConfirm"]) {
|
|
183
|
+
delete item["passwordConfirm"];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
await this.repository.createMany(data, user);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
this._logger.error(e);
|
|
189
|
+
throw e;
|
|
190
|
+
}
|
|
191
|
+
return data;
|
|
192
|
+
}
|
|
193
|
+
async readById(id, user) {
|
|
194
|
+
try {
|
|
195
|
+
this.permissionService.valid("read", user);
|
|
196
|
+
const result = await this.repository.getById(id);
|
|
197
|
+
delete result["password"];
|
|
198
|
+
return result;
|
|
199
|
+
} catch (e) {
|
|
200
|
+
this._logger.error(e);
|
|
201
|
+
throw e;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async read(criteria, options, user) {
|
|
205
|
+
try {
|
|
206
|
+
this.permissionService.valid("read", user);
|
|
207
|
+
const result = await this.repository.getByCriteria(criteria, options);
|
|
208
|
+
result.data.forEach((item) => delete item["password"]);
|
|
209
|
+
return result;
|
|
210
|
+
} catch (e) {
|
|
211
|
+
this._logger.error(e);
|
|
212
|
+
throw e;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
readBySpec(spec, options, user) {
|
|
216
|
+
return this.read(spec.criteria, options, user);
|
|
217
|
+
}
|
|
218
|
+
async update(id, data, user) {
|
|
219
|
+
try {
|
|
220
|
+
data.id = id;
|
|
221
|
+
this.permissionService.valid("update", user);
|
|
222
|
+
castModel(data, "update", user.permissions);
|
|
223
|
+
this.checkValidUpdate(data, user.permissions);
|
|
224
|
+
if (data["password"]) {
|
|
225
|
+
data["password"] = await PasswordService.hash(data["password"]);
|
|
226
|
+
}
|
|
227
|
+
if (data["passwordConfirm"]) {
|
|
228
|
+
delete data["passwordConfirm"];
|
|
229
|
+
}
|
|
230
|
+
await this.repository.update(data, user);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
this._logger.error(e);
|
|
233
|
+
throw e;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async updatePartial(id, data, user) {
|
|
237
|
+
try {
|
|
238
|
+
data.id = id;
|
|
239
|
+
this.permissionService.valid("update", user);
|
|
240
|
+
castModel(data, "update", user.permissions);
|
|
241
|
+
this.checkValidUpdatePartial(data, user.permissions);
|
|
242
|
+
if (data["password"]) {
|
|
243
|
+
data["password"] = await PasswordService.hash(data["password"]);
|
|
244
|
+
}
|
|
245
|
+
if (data["passwordConfirm"]) {
|
|
246
|
+
delete data["passwordConfirm"];
|
|
247
|
+
}
|
|
248
|
+
await this.repository.updatePartial(data, user);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
this._logger.error(e);
|
|
251
|
+
throw e;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async delete(id, user) {
|
|
255
|
+
try {
|
|
256
|
+
this.permissionService.valid("delete", user);
|
|
257
|
+
await this.repository.delete(id, user);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
this._logger.error(e);
|
|
260
|
+
throw e;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async uploadAttachment(data, options) {
|
|
264
|
+
if (!data.id) {
|
|
265
|
+
data.id = GuidService.create();
|
|
266
|
+
}
|
|
267
|
+
let oldId = null;
|
|
268
|
+
if (options?.start) {
|
|
269
|
+
oldId = data.id;
|
|
270
|
+
const stream = await this.attachmentRepository.getStream(data.id, {
|
|
271
|
+
start: 0,
|
|
272
|
+
end: options.start - 1
|
|
273
|
+
});
|
|
274
|
+
const combinedStream = CombinedStream.create();
|
|
275
|
+
combinedStream.append(stream);
|
|
276
|
+
combinedStream.append(data.stream);
|
|
277
|
+
data.stream = combinedStream;
|
|
278
|
+
data.id = GuidService.create();
|
|
279
|
+
}
|
|
280
|
+
this.attachmentRepository.upload(data, options);
|
|
281
|
+
if (oldId)
|
|
282
|
+
await this.attachmentRepository.delete(data.id);
|
|
283
|
+
return data.id;
|
|
284
|
+
}
|
|
285
|
+
getAttachmentInfo(id) {
|
|
286
|
+
return this.attachmentRepository.getInfo(id);
|
|
287
|
+
}
|
|
288
|
+
getAttachmentStream(id, options) {
|
|
289
|
+
return this.attachmentRepository.getStream(id, options);
|
|
290
|
+
}
|
|
291
|
+
async deleteAttachment(id) {
|
|
292
|
+
return this.attachmentRepository.delete(id);
|
|
293
|
+
}
|
|
294
|
+
changes(criteria) {
|
|
295
|
+
return this.repository.changesByCriteria(criteria);
|
|
296
|
+
}
|
|
297
|
+
checkValidCreate(item, permissions) {
|
|
298
|
+
const array = getInvalidFields(item, "create", permissions);
|
|
299
|
+
if (array.length) {
|
|
300
|
+
throw new DomainValidationError("Required fields: " + array.join(", "));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
checkValidUpdate(item, permissions) {
|
|
304
|
+
const array = getInvalidFields(item, "update", permissions);
|
|
305
|
+
if (array.length) {
|
|
306
|
+
throw new DomainValidationError("Required fields: " + array.join(", "));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
checkValidUpdatePartial(item, permissions) {
|
|
310
|
+
if (!isModel(item))
|
|
311
|
+
return;
|
|
312
|
+
const keys = Object.keys(item);
|
|
313
|
+
const array = getInvalidFields(item, "update", permissions).filter(
|
|
314
|
+
(invalidField) => keys.some((key) => key === invalidField)
|
|
315
|
+
);
|
|
316
|
+
if (array.length) {
|
|
317
|
+
throw new DomainValidationError("Required fields: " + array.join(", "));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
__decorateClass([
|
|
322
|
+
Memoize()
|
|
323
|
+
], CrudService.prototype, "getAttachmentInfo", 1);
|
|
324
|
+
CrudService = __decorateClass([
|
|
325
|
+
Injectable()
|
|
326
|
+
], CrudService);
|
|
327
|
+
|
|
328
|
+
// packages/crud/shell/app-services/src/lib/services/index.ts
|
|
329
|
+
var SERVICES = [CrudService];
|
|
330
|
+
export {
|
|
331
|
+
CrudService,
|
|
332
|
+
SERVICES
|
|
333
|
+
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { ICreateManyOptions } from '@smartsoft001/crud-domain';
|
|
3
|
+
import { ItemChangedData } from '@smartsoft001/crud-shell-dtos';
|
|
4
|
+
import { IAttachmentRepository, IEntity, IItemRepository, ISpecification } from '@smartsoft001/domain-core';
|
|
5
|
+
import { PermissionService } from '@smartsoft001/nestjs';
|
|
6
|
+
import { IUser } from '@smartsoft001/users';
|
|
7
|
+
import { Readable, Stream } from 'stream';
|
|
8
|
+
export declare class CrudService<T extends IEntity<string>> {
|
|
9
|
+
protected readonly permissionService: PermissionService;
|
|
10
|
+
protected readonly repository: IItemRepository<T>;
|
|
11
|
+
protected readonly attachmentRepository: IAttachmentRepository<T>;
|
|
12
|
+
private _logger;
|
|
13
|
+
constructor(permissionService: PermissionService, repository: IItemRepository<T>, attachmentRepository: IAttachmentRepository<T>);
|
|
14
|
+
create(data: T, user: IUser): Promise<string>;
|
|
15
|
+
createMany(data: T[], user: IUser, options: ICreateManyOptions): Promise<T[]>;
|
|
16
|
+
readById(id: string, user: IUser): Promise<T>;
|
|
17
|
+
read(criteria: any, options: any, user: IUser): Promise<{
|
|
18
|
+
data: T[];
|
|
19
|
+
totalCount: number;
|
|
20
|
+
}>;
|
|
21
|
+
readBySpec(spec: ISpecification, options: any, user: IUser): Promise<{
|
|
22
|
+
data: T[];
|
|
23
|
+
totalCount: number;
|
|
24
|
+
}>;
|
|
25
|
+
update(id: string, data: T, user: IUser): Promise<void>;
|
|
26
|
+
updatePartial(id: string, data: Partial<T>, user: IUser): Promise<void>;
|
|
27
|
+
delete(id: string, user: IUser): Promise<void>;
|
|
28
|
+
uploadAttachment(data: {
|
|
29
|
+
id: string;
|
|
30
|
+
fileName: string;
|
|
31
|
+
stream: Stream;
|
|
32
|
+
mimeType: string;
|
|
33
|
+
encoding: string;
|
|
34
|
+
}, options?: {
|
|
35
|
+
streamCallback?: (r: any) => void;
|
|
36
|
+
start?: number;
|
|
37
|
+
}): Promise<string>;
|
|
38
|
+
getAttachmentInfo(id: string): Promise<{
|
|
39
|
+
fileName: string;
|
|
40
|
+
contentType: string;
|
|
41
|
+
length: number;
|
|
42
|
+
}>;
|
|
43
|
+
getAttachmentStream(id: string, options?: {
|
|
44
|
+
start: number;
|
|
45
|
+
end: number;
|
|
46
|
+
}): Promise<Readable>;
|
|
47
|
+
deleteAttachment(id: string): Promise<void>;
|
|
48
|
+
changes(criteria: {
|
|
49
|
+
id?: string;
|
|
50
|
+
}): Observable<ItemChangedData>;
|
|
51
|
+
private checkValidCreate;
|
|
52
|
+
private checkValidUpdate;
|
|
53
|
+
private checkValidUpdatePartial;
|
|
54
|
+
}
|
package/.eslintrc
DELETED
package/jest.config.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
export default {
|
|
3
|
-
globals: {},
|
|
4
|
-
transform: {
|
|
5
|
-
'^.+\\.[tj]sx?$': [
|
|
6
|
-
'ts-jest',
|
|
7
|
-
{
|
|
8
|
-
tsConfig: '<rootDir>/tsconfig.spec.json',
|
|
9
|
-
},
|
|
10
|
-
],
|
|
11
|
-
},
|
|
12
|
-
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
|
13
|
-
coverageDirectory: '../../../../coverage/packages/crud/shell/app-services',
|
|
14
|
-
displayName: 'crud-shell-app-services',
|
|
15
|
-
testEnvironment: 'node',
|
|
16
|
-
preset: '../../../../jest.preset.js',
|
|
17
|
-
/* TODO: Update to latest Jest snapshotFormat
|
|
18
|
-
* By default Nx has kept the older style of Jest Snapshot formats
|
|
19
|
-
* to prevent breaking of any existing tests with snapshots.
|
|
20
|
-
* It's recommend you update to the latest format.
|
|
21
|
-
* You can do this by removing snapshotFormat property
|
|
22
|
-
* and running tests with --update-snapshot flag.
|
|
23
|
-
* Example: From within the project directory, run "nx test --update-snapshot"
|
|
24
|
-
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
|
25
|
-
*/
|
|
26
|
-
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
|
27
|
-
};
|
package/project.json
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "crud-shell-app-services",
|
|
3
|
-
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/crud/shell/app-services/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"tags": ["scope:crud", "type:shell"],
|
|
7
|
-
"generators": {},
|
|
8
|
-
"targets": {
|
|
9
|
-
"lint": {
|
|
10
|
-
"executor": "@nx/eslint:lint",
|
|
11
|
-
"outputs": ["{options.outputFile}"],
|
|
12
|
-
"options": {
|
|
13
|
-
"lintFilePatterns": [
|
|
14
|
-
"packages/crud/shell/app-services/**/*.{ts,tsx,js,jsx}",
|
|
15
|
-
"packages/crud/shell/app-services/package.json"
|
|
16
|
-
],
|
|
17
|
-
"tsConfig": [
|
|
18
|
-
"packages/crud/shell/app-services/tsconfig.lib.json",
|
|
19
|
-
"packages/crud/shell/app-services/tsconfig.spec.json"
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"test": {
|
|
24
|
-
"executor": "@nx/jest:jest",
|
|
25
|
-
"options": {
|
|
26
|
-
"jestConfig": "packages/crud/shell/app-services/jest.config.ts",
|
|
27
|
-
"passWithNoTests": true
|
|
28
|
-
},
|
|
29
|
-
"outputs": ["{workspaceRoot}/coverage/packages/crud/shell/app-services"]
|
|
30
|
-
},
|
|
31
|
-
"build": {
|
|
32
|
-
"executor": "@nx/esbuild:esbuild",
|
|
33
|
-
"options": {
|
|
34
|
-
"outputPath": "dist/packages/crud/shell/app-services",
|
|
35
|
-
"tsConfig": "packages/crud/shell/app-services/tsconfig.lib.json",
|
|
36
|
-
"packageJson": "packages/crud/shell/app-services/package.json",
|
|
37
|
-
"main": "packages/crud/shell/app-services/src/index.ts",
|
|
38
|
-
"assets": ["packages/crud/shell/app-services/*.md"]
|
|
39
|
-
},
|
|
40
|
-
"outputs": ["{options.outputPath}"]
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { of, Observable } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
import { CrudService } from './crud.service';
|
|
4
|
-
|
|
5
|
-
jest.mock('@smartsoft001/utils', () => ({
|
|
6
|
-
PasswordService: { hash: jest.fn(async (v) => 'hashed-' + v) },
|
|
7
|
-
GuidService: { create: jest.fn(() => 'guid') },
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
const mockUser = { id: 'u', username: 'test', permissions: ['p'] };
|
|
11
|
-
const mockData = { id: 'id', password: 'pw', passwordConfirm: 'pw' };
|
|
12
|
-
const mockRepo = () => ({
|
|
13
|
-
create: jest.fn(),
|
|
14
|
-
createMany: jest.fn(),
|
|
15
|
-
getById: jest.fn().mockResolvedValue({ id: 'id', password: 'pw' }),
|
|
16
|
-
getByCriteria: jest
|
|
17
|
-
.fn()
|
|
18
|
-
.mockResolvedValue({ data: [{ id: 'id', password: 'pw' }], totalCount: 1 }),
|
|
19
|
-
update: jest.fn(),
|
|
20
|
-
updatePartial: jest.fn(),
|
|
21
|
-
delete: jest.fn(),
|
|
22
|
-
clear: jest.fn(),
|
|
23
|
-
changesByCriteria: jest.fn(() => of({})),
|
|
24
|
-
});
|
|
25
|
-
const mockAttachRepo = () => ({
|
|
26
|
-
upload: jest.fn(),
|
|
27
|
-
getInfo: jest
|
|
28
|
-
.fn()
|
|
29
|
-
.mockResolvedValue({ fileName: 'f', contentType: 'c', length: 1 }),
|
|
30
|
-
getStream: jest.fn().mockResolvedValue({}),
|
|
31
|
-
delete: jest.fn(),
|
|
32
|
-
});
|
|
33
|
-
const mockPerm = () => ({ valid: jest.fn() });
|
|
34
|
-
|
|
35
|
-
describe('crud-app-services: CrudService', () => {
|
|
36
|
-
let service: CrudService<any>;
|
|
37
|
-
let repository: any;
|
|
38
|
-
let attachmentRepository: any;
|
|
39
|
-
let permissionService: any;
|
|
40
|
-
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
repository = mockRepo();
|
|
43
|
-
attachmentRepository = mockAttachRepo();
|
|
44
|
-
permissionService = mockPerm();
|
|
45
|
-
service = new CrudService(
|
|
46
|
-
permissionService,
|
|
47
|
-
repository,
|
|
48
|
-
attachmentRepository,
|
|
49
|
-
);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe('create', () => {
|
|
53
|
-
it('should create and return id', async () => {
|
|
54
|
-
const id = await service.create({ ...mockData }, mockUser);
|
|
55
|
-
expect(typeof id).toBe('string');
|
|
56
|
-
});
|
|
57
|
-
it('should log and throw on error', async () => {
|
|
58
|
-
permissionService.valid.mockImplementation(() => {
|
|
59
|
-
throw new Error('fail');
|
|
60
|
-
});
|
|
61
|
-
await expect(service.create({ ...mockData }, mockUser)).rejects.toThrow(
|
|
62
|
-
'fail',
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('createMany', () => {
|
|
68
|
-
it('should create many and return data', async () => {
|
|
69
|
-
const data = await service.createMany([{ ...mockData }], mockUser, {
|
|
70
|
-
mode: undefined,
|
|
71
|
-
});
|
|
72
|
-
expect(Array.isArray(data)).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
it('should clear repo if mode is replace', async () => {
|
|
75
|
-
await service.createMany([{ ...mockData }], mockUser, {
|
|
76
|
-
mode: 'replace',
|
|
77
|
-
});
|
|
78
|
-
expect(repository.clear).toHaveBeenCalled();
|
|
79
|
-
});
|
|
80
|
-
it('should log and throw on error', async () => {
|
|
81
|
-
permissionService.valid.mockImplementation(() => {
|
|
82
|
-
throw new Error('fail');
|
|
83
|
-
});
|
|
84
|
-
await expect(
|
|
85
|
-
service.createMany([{ ...mockData }], mockUser, { mode: undefined }),
|
|
86
|
-
).rejects.toThrow('fail');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('readById', () => {
|
|
91
|
-
it('should return result without password', async () => {
|
|
92
|
-
const res = await service.readById('id', mockUser);
|
|
93
|
-
expect(res.password).toBeUndefined();
|
|
94
|
-
});
|
|
95
|
-
it('should log and throw on error', async () => {
|
|
96
|
-
repository.getById.mockImplementation(() => {
|
|
97
|
-
throw new Error('fail');
|
|
98
|
-
});
|
|
99
|
-
await expect(service.readById('id', mockUser)).rejects.toThrow('fail');
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('read', () => {
|
|
104
|
-
it('should return data and totalCount', async () => {
|
|
105
|
-
const res = await service.read({}, {}, mockUser);
|
|
106
|
-
expect(res).toHaveProperty('data');
|
|
107
|
-
});
|
|
108
|
-
it('should log and throw on error', async () => {
|
|
109
|
-
repository.getByCriteria.mockImplementation(() => {
|
|
110
|
-
throw new Error('fail');
|
|
111
|
-
});
|
|
112
|
-
await expect(service.read({}, {}, mockUser)).rejects.toThrow('fail');
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('readBySpec', () => {
|
|
117
|
-
it('should call read with spec', async () => {
|
|
118
|
-
const spy = jest
|
|
119
|
-
.spyOn(service, 'read')
|
|
120
|
-
.mockResolvedValue({ data: [], totalCount: 0 });
|
|
121
|
-
await service.readBySpec({ criteria: {} }, {}, mockUser);
|
|
122
|
-
expect(spy).toHaveBeenCalled();
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe('update', () => {
|
|
127
|
-
it('should update', async () => {
|
|
128
|
-
await service.update('id', { ...mockData }, mockUser);
|
|
129
|
-
expect(repository.update).toHaveBeenCalled();
|
|
130
|
-
});
|
|
131
|
-
it('should log and throw on error', async () => {
|
|
132
|
-
repository.update.mockImplementation(() => {
|
|
133
|
-
throw new Error('fail');
|
|
134
|
-
});
|
|
135
|
-
await expect(
|
|
136
|
-
service.update('id', { ...mockData }, mockUser),
|
|
137
|
-
).rejects.toThrow('fail');
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('updatePartial', () => {
|
|
142
|
-
it('should updatePartial', async () => {
|
|
143
|
-
await service.updatePartial('id', { ...mockData }, mockUser);
|
|
144
|
-
expect(repository.updatePartial).toHaveBeenCalled();
|
|
145
|
-
});
|
|
146
|
-
it('should log and throw on error', async () => {
|
|
147
|
-
repository.updatePartial.mockImplementation(() => {
|
|
148
|
-
throw new Error('fail');
|
|
149
|
-
});
|
|
150
|
-
await expect(
|
|
151
|
-
service.updatePartial('id', { ...mockData }, mockUser),
|
|
152
|
-
).rejects.toThrow('fail');
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('delete', () => {
|
|
157
|
-
it('should delete', async () => {
|
|
158
|
-
await service.delete('id', mockUser);
|
|
159
|
-
expect(repository.delete).toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
it('should log and throw on error', async () => {
|
|
162
|
-
repository.delete.mockImplementation(() => {
|
|
163
|
-
throw new Error('fail');
|
|
164
|
-
});
|
|
165
|
-
await expect(service.delete('id', mockUser)).rejects.toThrow('fail');
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('uploadAttachment', () => {
|
|
170
|
-
it('should upload and return id', async () => {
|
|
171
|
-
const fakeStream = {
|
|
172
|
-
pipe: jest.fn(),
|
|
173
|
-
addListener: jest.fn(),
|
|
174
|
-
on: jest.fn(),
|
|
175
|
-
once: jest.fn(),
|
|
176
|
-
emit: jest.fn(),
|
|
177
|
-
removeListener: jest.fn(),
|
|
178
|
-
destroy: jest.fn(),
|
|
179
|
-
off: jest.fn(),
|
|
180
|
-
removeAllListeners: jest.fn(),
|
|
181
|
-
setMaxListeners: jest.fn(),
|
|
182
|
-
getMaxListeners: jest.fn(),
|
|
183
|
-
listeners: jest.fn(),
|
|
184
|
-
rawListeners: jest.fn(),
|
|
185
|
-
prependListener: jest.fn(),
|
|
186
|
-
prependOnceListener: jest.fn(),
|
|
187
|
-
eventNames: jest.fn(),
|
|
188
|
-
listenerCount: jest.fn(),
|
|
189
|
-
};
|
|
190
|
-
const id = await service.uploadAttachment({
|
|
191
|
-
id: '',
|
|
192
|
-
fileName: '',
|
|
193
|
-
stream: fakeStream,
|
|
194
|
-
mimeType: '',
|
|
195
|
-
encoding: '',
|
|
196
|
-
});
|
|
197
|
-
expect(typeof id).toBe('string');
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('getAttachmentInfo', () => {
|
|
202
|
-
it('should get info', async () => {
|
|
203
|
-
const info = await service.getAttachmentInfo('id');
|
|
204
|
-
expect(info).toHaveProperty('fileName');
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe('getAttachmentStream', () => {
|
|
209
|
-
it('should get stream', async () => {
|
|
210
|
-
const stream = await service.getAttachmentStream('id');
|
|
211
|
-
expect(stream).toBeDefined();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('deleteAttachment', () => {
|
|
216
|
-
it('should delete attachment', async () => {
|
|
217
|
-
await service.deleteAttachment('id');
|
|
218
|
-
expect(attachmentRepository.delete).toHaveBeenCalled();
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('changes', () => {
|
|
223
|
-
it('should return observable', () => {
|
|
224
|
-
const obs = service.changes({});
|
|
225
|
-
expect(obs instanceof Observable).toBe(true);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
-
import * as CombinedStream from 'combined-stream';
|
|
3
|
-
import { Guid } from 'guid-typescript';
|
|
4
|
-
import { Memoize } from 'lodash-decorators';
|
|
5
|
-
import { Observable } from 'rxjs';
|
|
6
|
-
|
|
7
|
-
import { ICreateManyOptions } from '@smartsoft001/crud-domain';
|
|
8
|
-
import { ItemChangedData } from '@smartsoft001/crud-shell-dtos';
|
|
9
|
-
import {
|
|
10
|
-
DomainValidationError,
|
|
11
|
-
IAttachmentRepository,
|
|
12
|
-
IEntity,
|
|
13
|
-
IItemRepository,
|
|
14
|
-
ISpecification,
|
|
15
|
-
} from '@smartsoft001/domain-core';
|
|
16
|
-
import { castModel, getInvalidFields, isModel } from '@smartsoft001/models';
|
|
17
|
-
import { PermissionService } from '@smartsoft001/nestjs';
|
|
18
|
-
import { IUser } from '@smartsoft001/users';
|
|
19
|
-
import { GuidService, PasswordService } from '@smartsoft001/utils';
|
|
20
|
-
|
|
21
|
-
import { Readable, Stream } from 'stream';
|
|
22
|
-
|
|
23
|
-
@Injectable()
|
|
24
|
-
export class CrudService<T extends IEntity<string>> {
|
|
25
|
-
private _logger = new Logger(CrudService.name, { timestamp: true });
|
|
26
|
-
|
|
27
|
-
constructor(
|
|
28
|
-
protected readonly permissionService: PermissionService,
|
|
29
|
-
protected readonly repository: IItemRepository<T>,
|
|
30
|
-
protected readonly attachmentRepository: IAttachmentRepository<T>,
|
|
31
|
-
) {}
|
|
32
|
-
|
|
33
|
-
async create(data: T, user: IUser): Promise<string> {
|
|
34
|
-
data.id = Guid.raw();
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
this.permissionService.valid('create', user);
|
|
38
|
-
|
|
39
|
-
castModel(data, 'create', user.permissions);
|
|
40
|
-
this.checkValidCreate(data, user.permissions);
|
|
41
|
-
|
|
42
|
-
if (data['password']) {
|
|
43
|
-
data['password'] = await PasswordService.hash(data['password']);
|
|
44
|
-
}
|
|
45
|
-
if (data['passwordConfirm']) {
|
|
46
|
-
delete data['passwordConfirm'];
|
|
47
|
-
}
|
|
48
|
-
await this.repository.create(data, user);
|
|
49
|
-
|
|
50
|
-
return data.id;
|
|
51
|
-
} catch (e) {
|
|
52
|
-
this._logger.error(e);
|
|
53
|
-
throw e;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async createMany(
|
|
58
|
-
data: T[],
|
|
59
|
-
user: IUser,
|
|
60
|
-
options: ICreateManyOptions,
|
|
61
|
-
): Promise<T[]> {
|
|
62
|
-
data.forEach((item) => {
|
|
63
|
-
item.id = Guid.raw();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
this.permissionService.valid('create', user);
|
|
68
|
-
|
|
69
|
-
data.forEach((item) => {
|
|
70
|
-
castModel(item, 'create', user.permissions);
|
|
71
|
-
this.checkValidCreate(item, user.permissions);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
if (options && options.mode === 'replace') {
|
|
75
|
-
await this.repository.clear(user);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
for (let index = 0; index < data.length; index++) {
|
|
79
|
-
const item = data[index];
|
|
80
|
-
|
|
81
|
-
if (item['password']) {
|
|
82
|
-
item['password'] = await PasswordService.hash(item['password']);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (item['passwordConfirm']) {
|
|
86
|
-
delete item['passwordConfirm'];
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
await this.repository.createMany(data, user);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
this._logger.error(e);
|
|
93
|
-
throw e;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return data;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async readById(id: string, user: IUser): Promise<T> {
|
|
100
|
-
try {
|
|
101
|
-
this.permissionService.valid('read', user);
|
|
102
|
-
const result = await this.repository.getById(id);
|
|
103
|
-
delete result['password'];
|
|
104
|
-
|
|
105
|
-
return result;
|
|
106
|
-
} catch (e) {
|
|
107
|
-
this._logger.error(e);
|
|
108
|
-
throw e;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async read(
|
|
113
|
-
criteria: any,
|
|
114
|
-
options: any,
|
|
115
|
-
user: IUser,
|
|
116
|
-
): Promise<{ data: T[]; totalCount: number }> {
|
|
117
|
-
try {
|
|
118
|
-
this.permissionService.valid('read', user);
|
|
119
|
-
const result = await this.repository.getByCriteria(criteria, options);
|
|
120
|
-
result.data.forEach((item) => delete item['password']);
|
|
121
|
-
|
|
122
|
-
return result;
|
|
123
|
-
} catch (e) {
|
|
124
|
-
this._logger.error(e);
|
|
125
|
-
throw e;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
readBySpec(
|
|
130
|
-
spec: ISpecification,
|
|
131
|
-
options: any,
|
|
132
|
-
user: IUser,
|
|
133
|
-
): Promise<{ data: T[]; totalCount: number }> {
|
|
134
|
-
return this.read(spec.criteria, options, user);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async update(id: string, data: T, user: IUser): Promise<void> {
|
|
138
|
-
try {
|
|
139
|
-
data.id = id;
|
|
140
|
-
this.permissionService.valid('update', user);
|
|
141
|
-
|
|
142
|
-
castModel(data, 'update', user.permissions);
|
|
143
|
-
this.checkValidUpdate(data, user.permissions);
|
|
144
|
-
|
|
145
|
-
if (data['password']) {
|
|
146
|
-
data['password'] = await PasswordService.hash(data['password']);
|
|
147
|
-
}
|
|
148
|
-
if (data['passwordConfirm']) {
|
|
149
|
-
delete data['passwordConfirm'];
|
|
150
|
-
}
|
|
151
|
-
await this.repository.update(data, user);
|
|
152
|
-
} catch (e) {
|
|
153
|
-
this._logger.error(e);
|
|
154
|
-
throw e;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async updatePartial(
|
|
159
|
-
id: string,
|
|
160
|
-
data: Partial<T>,
|
|
161
|
-
user: IUser,
|
|
162
|
-
): Promise<void> {
|
|
163
|
-
try {
|
|
164
|
-
data.id = id;
|
|
165
|
-
|
|
166
|
-
this.permissionService.valid('update', user);
|
|
167
|
-
|
|
168
|
-
castModel(data, 'update', user.permissions);
|
|
169
|
-
this.checkValidUpdatePartial(data, user.permissions);
|
|
170
|
-
|
|
171
|
-
if (data['password']) {
|
|
172
|
-
data['password'] = await PasswordService.hash(data['password']);
|
|
173
|
-
}
|
|
174
|
-
if (data['passwordConfirm']) {
|
|
175
|
-
delete data['passwordConfirm'];
|
|
176
|
-
}
|
|
177
|
-
await this.repository.updatePartial(data as Partial<T> & { id }, user);
|
|
178
|
-
} catch (e) {
|
|
179
|
-
this._logger.error(e);
|
|
180
|
-
throw e;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async delete(id: string, user: IUser): Promise<void> {
|
|
185
|
-
try {
|
|
186
|
-
this.permissionService.valid('delete', user);
|
|
187
|
-
|
|
188
|
-
await this.repository.delete(id, user);
|
|
189
|
-
} catch (e) {
|
|
190
|
-
this._logger.error(e);
|
|
191
|
-
throw e;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async uploadAttachment(
|
|
196
|
-
data: {
|
|
197
|
-
id: string;
|
|
198
|
-
fileName: string;
|
|
199
|
-
stream: Stream;
|
|
200
|
-
mimeType: string;
|
|
201
|
-
encoding: string;
|
|
202
|
-
},
|
|
203
|
-
options?: { streamCallback?: (r) => void; start?: number },
|
|
204
|
-
): Promise<string> {
|
|
205
|
-
if (!data.id) {
|
|
206
|
-
data.id = GuidService.create();
|
|
207
|
-
}
|
|
208
|
-
let oldId = null;
|
|
209
|
-
|
|
210
|
-
if (options?.start) {
|
|
211
|
-
oldId = data.id;
|
|
212
|
-
const stream = await this.attachmentRepository.getStream(data.id, {
|
|
213
|
-
start: 0,
|
|
214
|
-
end: options.start - 1,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
const combinedStream = CombinedStream.create();
|
|
218
|
-
combinedStream.append(stream);
|
|
219
|
-
combinedStream.append(data.stream);
|
|
220
|
-
|
|
221
|
-
data.stream = combinedStream;
|
|
222
|
-
data.id = GuidService.create();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
this.attachmentRepository.upload(data, options);
|
|
226
|
-
|
|
227
|
-
if (oldId) await this.attachmentRepository.delete(data.id);
|
|
228
|
-
|
|
229
|
-
return data.id;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
@Memoize()
|
|
233
|
-
getAttachmentInfo(
|
|
234
|
-
id: string,
|
|
235
|
-
): Promise<{ fileName: string; contentType: string; length: number }> {
|
|
236
|
-
return this.attachmentRepository.getInfo(id);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
getAttachmentStream(
|
|
240
|
-
id: string,
|
|
241
|
-
options?: { start: number; end: number },
|
|
242
|
-
): Promise<Readable> {
|
|
243
|
-
return this.attachmentRepository.getStream(id, options);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
async deleteAttachment(id: string): Promise<void> {
|
|
247
|
-
return this.attachmentRepository.delete(id);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
changes(criteria: { id?: string }): Observable<ItemChangedData> {
|
|
251
|
-
return this.repository.changesByCriteria(criteria);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
private checkValidCreate(item: T, permissions: Array<string>): void {
|
|
255
|
-
const array = getInvalidFields(item, 'create', permissions);
|
|
256
|
-
|
|
257
|
-
if (array.length) {
|
|
258
|
-
throw new DomainValidationError('Required fields: ' + array.join(', '));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
private checkValidUpdate(item: T, permissions: Array<string>): void {
|
|
263
|
-
const array = getInvalidFields(item, 'update', permissions);
|
|
264
|
-
|
|
265
|
-
if (array.length) {
|
|
266
|
-
throw new DomainValidationError('Required fields: ' + array.join(', '));
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private checkValidUpdatePartial(
|
|
271
|
-
item: Partial<T>,
|
|
272
|
-
permissions: Array<string>,
|
|
273
|
-
): void {
|
|
274
|
-
if (!isModel(item)) return;
|
|
275
|
-
|
|
276
|
-
const keys = Object.keys(item);
|
|
277
|
-
const array = getInvalidFields(item, 'update', permissions).filter(
|
|
278
|
-
(invalidField) => keys.some((key) => key === invalidField),
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
if (array.length) {
|
|
282
|
-
throw new DomainValidationError('Required fields: ' + array.join(', '));
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
package/tsconfig.json
DELETED
package/tsconfig.lib.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"outDir": "../../../../dist/out-tsc",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"types": ["node"]
|
|
8
|
-
},
|
|
9
|
-
"exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
|
|
10
|
-
"include": ["**/*.ts"]
|
|
11
|
-
}
|
package/tsconfig.spec.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../../../dist/out-tsc",
|
|
5
|
-
"module": "commonjs",
|
|
6
|
-
"types": ["jest", "node"]
|
|
7
|
-
},
|
|
8
|
-
"include": [
|
|
9
|
-
"**/*.spec.ts",
|
|
10
|
-
"**/*.test.ts",
|
|
11
|
-
"**/*.spec.tsx",
|
|
12
|
-
"**/*.test.tsx",
|
|
13
|
-
"**/*.spec.js",
|
|
14
|
-
"**/*.test.js",
|
|
15
|
-
"**/*.spec.jsx",
|
|
16
|
-
"**/*.test.jsx",
|
|
17
|
-
"**/*.d.ts",
|
|
18
|
-
"jest.config.ts"
|
|
19
|
-
]
|
|
20
|
-
}
|
|
File without changes
|
|
File without changes
|