@wisemen/nestjs-custom-fields 0.0.1 → 0.0.3
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/README.md +1 -1
- package/dist/factory/custom-field-value.factory.d.ts +4 -0
- package/dist/factory/custom-field-value.factory.js +8 -0
- package/dist/factory/custom-field-value.factory.js.map +1 -0
- package/dist/factory/custom-field-value.factory.unit.test.d.ts +1 -0
- package/dist/factory/custom-field-value.factory.unit.test.js +24 -0
- package/dist/factory/custom-field-value.factory.unit.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/repositories/custom-field-definitions.repository.d.ts +1 -1
- package/dist/repositories/custom-field-definitions.repository.js +7 -2
- package/dist/repositories/custom-field-definitions.repository.js.map +1 -1
- package/dist/typeorm/custom-field-value.column.d.ts +1 -1
- package/dist/typeorm/custom-field-value.column.js +6 -0
- package/dist/typeorm/custom-field-value.column.js.map +1 -1
- package/package.json +11 -11
- package/skills/getting-started/SKILL.md +61 -5
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Definition uniqueness is scoped by `entityType`. For non-tenant definitions,
|
|
|
17
17
|
## Step 1: Register The Exported Entity And Create The Table
|
|
18
18
|
|
|
19
19
|
This package already exports the effective `CustomFieldDefinition` TypeORM
|
|
20
|
-
entity. Add it to
|
|
20
|
+
entity. Add it to all TypeORM datasources yourself so TypeORM knows about the
|
|
21
21
|
table metadata. After that, generate and run a migration so the table and the
|
|
22
22
|
exported entity's partial unique indexes are created in your database.
|
|
23
23
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CustomFieldDefinitionUuid } from '#src/custom-field-definition.uuid.js';
|
|
2
|
+
import type { CustomFieldValueByType } from '#src/custom-field-value.js';
|
|
3
|
+
import { CustomFieldType } from '#src/enum/custom-field-type.enum.js';
|
|
4
|
+
export declare function customFieldValue<T extends CustomFieldType>(type: T, definitionUuid: CustomFieldDefinitionUuid, value: CustomFieldValueByType<T>['value']): CustomFieldValueByType<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-value.factory.js","sourceRoot":"","sources":["../../lib/factory/custom-field-value.factory.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,gBAAgB,CAC9B,IAAO,EACP,cAAyC,EACzC,KAAyC;IAEzC,OAAO;QACL,cAAc;QACd,IAAI;QACJ,KAAK;KACN,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import { expect } from 'expect';
|
|
3
|
+
import { CustomFieldType } from '#src/enum/custom-field-type.enum.js';
|
|
4
|
+
import { generateUuid } from '#src/custom-field-definition.uuid.js';
|
|
5
|
+
import { customFieldValue } from '#src/factory/custom-field-value.factory.js';
|
|
6
|
+
describe('customFieldValue', () => {
|
|
7
|
+
it('creates a typed custom field value', () => {
|
|
8
|
+
const definitionUuid = generateUuid();
|
|
9
|
+
expect(customFieldValue(CustomFieldType.TEXT, definitionUuid, 'hello')).toEqual({
|
|
10
|
+
definitionUuid,
|
|
11
|
+
type: CustomFieldType.TEXT,
|
|
12
|
+
value: 'hello'
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
it('supports values whose runtime shape depends on the field type', () => {
|
|
16
|
+
const definitionUuid = generateUuid();
|
|
17
|
+
expect(customFieldValue(CustomFieldType.MULTI_SELECT, definitionUuid, ['low', 'high'])).toEqual({
|
|
18
|
+
definitionUuid,
|
|
19
|
+
type: CustomFieldType.MULTI_SELECT,
|
|
20
|
+
value: ['low', 'high']
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=custom-field-value.factory.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-value.factory.unit.test.js","sourceRoot":"","sources":["../../lib/factory/custom-field-value.factory.unit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAA;AAE7E,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,cAAc,GAAG,YAAY,EAA6B,CAAA;QAEhE,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9E,cAAc;YACd,IAAI,EAAE,eAAe,CAAC,IAAI;YAC1B,KAAK,EAAE,OAAO;SACf,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,cAAc,GAAG,YAAY,EAA6B,CAAA;QAEhE,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9F,cAAc;YACd,IAAI,EAAE,eAAe,CAAC,YAAY;YAClC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { IsCustomFieldsValues as IsCustomFields } from './decorators/is-custom-f
|
|
|
8
8
|
export { CustomFieldValueApiExtraModels, CustomFieldValueDtoApiProperty, BooleanCustomFieldValueDto, CustomFieldValueDto, DateCustomFieldValueDto, TimestampCustomFieldValueDto, MonetaryCustomFieldValueDto, MultiSelectCustomFieldValueDto, NumberCustomFieldValueDto, SingleSelectCustomFieldValueDto, TextArrayCustomFieldValueDto, TextCustomFieldValueDto } from './dto/custom-field-value.dto.js';
|
|
9
9
|
export { CustomFieldDefinitionError } from './errors/custom-field-definition.error.js';
|
|
10
10
|
export { customFieldDefinition } from './factory/custom-field-definition.factory.js';
|
|
11
|
+
export { customFieldValue } from './factory/custom-field-value.factory.js';
|
|
11
12
|
export { CustomFieldDefinitionResponse } from './responses/custom-field-definition.response.js';
|
|
12
13
|
export { CustomFieldDefinitionData as CustomFieldDefinitionFields } from './custom-field-definition.js';
|
|
13
14
|
export { CustomFieldType, CustomFieldTypeApiProperty, CustomFieldTypeColumn } from './enum/custom-field-type.enum.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { IsCustomFieldsValues as IsCustomFields } from './decorators/is-custom-f
|
|
|
7
7
|
export { CustomFieldValueApiExtraModels, CustomFieldValueDtoApiProperty, BooleanCustomFieldValueDto, CustomFieldValueDto, DateCustomFieldValueDto, TimestampCustomFieldValueDto, MonetaryCustomFieldValueDto, MultiSelectCustomFieldValueDto, NumberCustomFieldValueDto, SingleSelectCustomFieldValueDto, TextArrayCustomFieldValueDto, TextCustomFieldValueDto } from './dto/custom-field-value.dto.js';
|
|
8
8
|
export { CustomFieldDefinitionError } from './errors/custom-field-definition.error.js';
|
|
9
9
|
export { customFieldDefinition } from './factory/custom-field-definition.factory.js';
|
|
10
|
+
export { customFieldValue } from './factory/custom-field-value.factory.js';
|
|
10
11
|
export { CustomFieldDefinitionResponse } from './responses/custom-field-definition.response.js';
|
|
11
12
|
export { CustomFieldType, CustomFieldTypeApiProperty, CustomFieldTypeColumn } from './enum/custom-field-type.enum.js';
|
|
12
13
|
export { CustomFieldDefinition } from './typeorm/custom-field-definition.entity.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAE5C,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,oBAAoB,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAA;AACnG,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAA;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,iDAAiD,CAAA;AAE/F,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAA;AAC/E,OAAO,EAAE,+BAA+B,EAAE,MAAM,qDAAqD,CAAA;AACrG,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,8BAA8B,CAAA;AAE5C,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,oBAAoB,IAAI,cAAc,EAAE,MAAM,4CAA4C,CAAA;AACnG,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAA;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAA;AAC1E,OAAO,EAAE,6BAA6B,EAAE,MAAM,iDAAiD,CAAA;AAE/F,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AACrH,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAA;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAA;AAC/E,OAAO,EAAE,+BAA+B,EAAE,MAAM,qDAAqD,CAAA;AACrG,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAA"}
|
|
@@ -2,7 +2,7 @@ import { Repository } from 'typeorm';
|
|
|
2
2
|
import { CustomFieldDefinition } from '#src/typeorm/custom-field-definition.entity.js';
|
|
3
3
|
export interface CustomFieldDefinitionsOptions {
|
|
4
4
|
tenantUuid?: string;
|
|
5
|
-
entityType?: string;
|
|
5
|
+
entityType?: string | string[];
|
|
6
6
|
}
|
|
7
7
|
export declare class CustomFieldDefinitionsRepository {
|
|
8
8
|
private readonly repository;
|
|
@@ -11,7 +11,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
13
|
import { Injectable } from '@nestjs/common';
|
|
14
|
-
import { Repository } from 'typeorm';
|
|
14
|
+
import { Any, Repository } from 'typeorm';
|
|
15
15
|
import { CustomFieldDefinition } from '#src/typeorm/custom-field-definition.entity.js';
|
|
16
16
|
import { InjectRepository } from '@wisemen/nestjs-typeorm';
|
|
17
17
|
let CustomFieldDefinitionsRepository = class CustomFieldDefinitionsRepository {
|
|
@@ -20,9 +20,14 @@ let CustomFieldDefinitionsRepository = class CustomFieldDefinitionsRepository {
|
|
|
20
20
|
this.repository = repository;
|
|
21
21
|
}
|
|
22
22
|
async findDefinitions(options) {
|
|
23
|
+
const entityType = options?.entityType;
|
|
24
|
+
const isArray = Array.isArray(entityType);
|
|
25
|
+
if (isArray && entityType.length === 0) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
23
28
|
return this.repository.find({
|
|
24
29
|
where: {
|
|
25
|
-
entityType:
|
|
30
|
+
entityType: isArray ? Any(entityType) : entityType,
|
|
26
31
|
tenantUuid: options?.tenantUuid
|
|
27
32
|
},
|
|
28
33
|
order: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-field-definitions.repository.js","sourceRoot":"","sources":["../../lib/repositories/custom-field-definitions.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"custom-field-definitions.repository.js","sourceRoot":"","sources":["../../lib/repositories/custom-field-definitions.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAA;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAQnD,IAAM,gCAAgC,GAAtC,MAAM,gCAAgC;IAGxB;IAFnB,YAEmB,UAA6C;QAA7C,eAAU,GAAV,UAAU,CAAmC;IAC5D,CAAC;IAEL,KAAK,CAAC,eAAe,CACnB,OAAuC;QAEvC,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,CAAA;QACtC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;QACzC,IAAI,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,CAAA;QACX,CAAC;QAED,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,KAAK,EAAE;gBACL,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU;gBAClD,UAAU,EAAE,OAAO,EAAE,UAAU;aAChC;YACD,KAAK,EAAE;gBACL,GAAG,EAAE,KAAK;aACX;SACF,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAzBY,gCAAgC;IAD5C,UAAU,EAAE;IAGR,WAAA,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;qCACX,UAAU;GAH9B,gCAAgC,CAyB5C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ColumnOptions } from 'typeorm';
|
|
2
|
-
export declare function CustomFieldValueColumn(options?: Omit<ColumnOptions, 'type' | 'transformer'
|
|
2
|
+
export declare function CustomFieldValueColumn(options?: Omit<ColumnOptions, 'type' | 'transformer'>): PropertyDecorator;
|
|
@@ -23,12 +23,18 @@ class CustomFieldValueTransformer {
|
|
|
23
23
|
this.timestampTransformer = transformers.timestampTransformer;
|
|
24
24
|
}
|
|
25
25
|
from(value) {
|
|
26
|
+
if (value == null) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
26
29
|
if (Array.isArray(value)) {
|
|
27
30
|
return value.map(v => this.parseCustomFieldValue(v));
|
|
28
31
|
}
|
|
29
32
|
return this.parseCustomFieldValue(value);
|
|
30
33
|
}
|
|
31
34
|
to(value) {
|
|
35
|
+
if (value == null) {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
32
38
|
if (Array.isArray(value)) {
|
|
33
39
|
return value.map(v => this.getSerializeValue(v));
|
|
34
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-field-value.column.js","sourceRoot":"","sources":["../../lib/typeorm/custom-field-value.column.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAY,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AACrE,OAAO,EAAE,MAAM,EAAwC,MAAM,SAAS,CAAA;AAEtE,MAAM,mBAAmB,GAAG,EAA6B,CAAA;AACzD,MAAM,iBAAiB,GAAG,IAAI,CAAA;AAO9B,MAAM,UAAU,sBAAsB,CAAC,
|
|
1
|
+
{"version":3,"file":"custom-field-value.column.js","sourceRoot":"","sources":["../../lib/typeorm/custom-field-value.column.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wisemen/nestjs-custom-fields",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -18,29 +18,29 @@
|
|
|
18
18
|
"skills/"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@wisemen/
|
|
22
|
-
"@wisemen/
|
|
23
|
-
"@wisemen/
|
|
24
|
-
"@wisemen/api-error": "
|
|
25
|
-
"@wisemen/validators": "
|
|
26
|
-
"@wisemen/nestjs-typeorm": "
|
|
21
|
+
"@wisemen/datewise": "^1.0.14",
|
|
22
|
+
"@wisemen/localized-string": "^0.1.1",
|
|
23
|
+
"@wisemen/monetary": "^0.5.2",
|
|
24
|
+
"@wisemen/api-error": "^1.0.0",
|
|
25
|
+
"@wisemen/validators": "^0.0.25",
|
|
26
|
+
"@wisemen/nestjs-typeorm": "^1.2.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"@nestjs/swagger": "^11.4.2",
|
|
30
|
-
"class-transformer": "^0.5.1",
|
|
31
|
-
"class-validator": "^0.15.1",
|
|
32
29
|
"@nestjs/common": "^11.1.19",
|
|
33
30
|
"@nestjs/core": "^11.1.19",
|
|
31
|
+
"@nestjs/swagger": "^11.4.2",
|
|
34
32
|
"@opentelemetry/api": "^1.9.1",
|
|
33
|
+
"class-transformer": "^0.5.1",
|
|
34
|
+
"class-validator": "^0.15.1",
|
|
35
35
|
"typeorm": "^1.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "25.3.0",
|
|
39
39
|
"eslint": "10.2.1",
|
|
40
|
+
"expect": "30.4.1",
|
|
40
41
|
"oxlint": "1.59.0",
|
|
41
42
|
"oxlint-tsgolint": "0.20.0",
|
|
42
43
|
"typescript": "^5.9.3",
|
|
43
|
-
"expect": "30.4.1",
|
|
44
44
|
"@wisemen/eslint-config-nestjs": "1.1.1"
|
|
45
45
|
},
|
|
46
46
|
"author": "Wisemen",
|
|
@@ -20,7 +20,7 @@ Definition uniqueness is scoped by `entityType`. For non-tenant definitions,
|
|
|
20
20
|
## Step 1: Register The Exported Entity And Create The Table
|
|
21
21
|
|
|
22
22
|
This package already exports the effective `CustomFieldDefinition` TypeORM
|
|
23
|
-
entity. Add it to
|
|
23
|
+
entity. Add it to all TypeORM datasources yourself so TypeORM knows about the
|
|
24
24
|
table metadata. After that, generate and run a migration so the table and the
|
|
25
25
|
exported entity's partial unique indexes are created in your database.
|
|
26
26
|
|
|
@@ -64,7 +64,8 @@ import {
|
|
|
64
64
|
} from '@wisemen/nestjs-custom-fields'
|
|
65
65
|
|
|
66
66
|
enum CustomFieldEntityType {
|
|
67
|
-
TICKET = 'ticket'
|
|
67
|
+
TICKET = 'ticket',
|
|
68
|
+
COMMENT = 'comment'
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
export class Playground extends JobContainer {
|
|
@@ -186,6 +187,49 @@ const parsed = dto.parse()
|
|
|
186
187
|
// }
|
|
187
188
|
```
|
|
188
189
|
|
|
190
|
+
## Creating Definitions And Values In Tests
|
|
191
|
+
|
|
192
|
+
Use `customFieldDefinition(...)` to create definitions in tests. This keeps
|
|
193
|
+
test fixtures on the same normalized and validated path as production
|
|
194
|
+
definitions. The factory generates the definition UUID, so use `definition.uuid`
|
|
195
|
+
when creating its values.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { LocalizedString } from '@wisemen/localized-string'
|
|
199
|
+
import {
|
|
200
|
+
CustomFieldType,
|
|
201
|
+
customFieldDefinition,
|
|
202
|
+
customFieldValue,
|
|
203
|
+
type CustomFieldValue
|
|
204
|
+
} from '@wisemen/nestjs-custom-fields'
|
|
205
|
+
|
|
206
|
+
const definition = customFieldDefinition(CustomFieldType.TEXT, {
|
|
207
|
+
tenantUuid: null,
|
|
208
|
+
entityType: 'ticket',
|
|
209
|
+
key: 'summary',
|
|
210
|
+
label: new LocalizedString([{ locale: 'en', value: 'Summary' }]),
|
|
211
|
+
description: null,
|
|
212
|
+
isRequired: false
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
const value: CustomFieldValue = customFieldValue(
|
|
216
|
+
CustomFieldType.TEXT,
|
|
217
|
+
definition.uuid,
|
|
218
|
+
'hello'
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For request-boundary tests, instantiate the relevant
|
|
223
|
+
`CustomFieldValueDto` subclass and call `.parse()` to create the domain value.
|
|
224
|
+
`CustomFieldValueDto.from(value)` is the inverse operation: it maps an existing
|
|
225
|
+
domain value to a response DTO and does not create a value.
|
|
226
|
+
|
|
227
|
+
There is no public builder for definitions or values. Use the two factories for
|
|
228
|
+
standard fixtures, and keep more specialized test builders local to the
|
|
229
|
+
consuming project. `customFieldValue(...)` only constructs the typed value; use
|
|
230
|
+
`validateCustomFieldValue(...)` or `validateCustomFieldValues(...)` to validate
|
|
231
|
+
it against its definition.
|
|
232
|
+
|
|
189
233
|
## Step 5: Retrieve Definitions For Validation
|
|
190
234
|
|
|
191
235
|
When you process submitted values, retrieve the matching definitions through
|
|
@@ -222,15 +266,27 @@ async update(...): Promise<void> {
|
|
|
222
266
|
```
|
|
223
267
|
On repository lookups, `tenantUuid` is optional. Omit it to work with
|
|
224
268
|
non-tenant definitions, or set it to retrieve tenant-specific definitions in a
|
|
225
|
-
multi-tenant setup.
|
|
269
|
+
multi-tenant setup. `entityType` is also optional and accepts either one entity
|
|
270
|
+
type or an array of entity types.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
const definitions = await repository.findDefinitions({
|
|
274
|
+
entityType: [CustomFieldEntityType.TICKET, CustomFieldEntityType.COMMENT],
|
|
275
|
+
tenantUuid
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Use a multi-entity lookup when a use case validates or returns definitions for
|
|
280
|
+
several entity types together. The tenant filter still applies to every
|
|
281
|
+
requested entity type, and results remain ordered by definition `key`.
|
|
226
282
|
|
|
227
283
|
After DTO parsing, validate the resolved values against the definitions that
|
|
228
284
|
apply for that entity and tenant. This is the runtime check that enforces the
|
|
229
285
|
definition rules.
|
|
230
286
|
|
|
231
287
|
The runtime order matters: call `.parse()` on the DTOs first, then run
|
|
232
|
-
`validateCustomFieldValues(...)` against the definitions for
|
|
233
|
-
|
|
288
|
+
`validateCustomFieldValues(...)` against the definitions for the relevant
|
|
289
|
+
entity type or entity types and tenant scope.
|
|
234
290
|
|
|
235
291
|
```ts
|
|
236
292
|
import { Injectable } from '@nestjs/common'
|