@opra/common 1.5.0 → 1.5.1
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/browser/index.cjs +5 -5
- package/browser/index.mjs +5 -5
- package/cjs/document/api-document.js +2 -3
- package/cjs/document/common/document-init-context.js +0 -5
- package/cjs/document/common/document-node.js +2 -3
- package/cjs/document/data-type/api-field.js +9 -18
- package/cjs/document/data-type/complex-type-base.js +16 -14
- package/cjs/document/data-type/complex-type.js +14 -7
- package/cjs/document/data-type/data-type.js +12 -14
- package/cjs/document/data-type/enum-type.js +12 -9
- package/cjs/document/data-type/extended-types/field-path.type.js +7 -7
- package/cjs/document/data-type/extended-types/filter.type.js +5 -5
- package/cjs/document/data-type/mapped-type.js +10 -4
- package/cjs/document/data-type/mixin-type.js +13 -4
- package/cjs/document/data-type/simple-type.js +11 -16
- package/cjs/document/decorators/api-field-decorator.js +37 -3
- package/cjs/document/decorators/simple-type.decorator.js +2 -0
- package/cjs/document/factory/api-document.factory.js +0 -1
- package/cjs/document/utils/test-scope-match.js +13 -0
- package/cjs/filter/filter-rules.js +6 -7
- package/esm/document/api-document.js +2 -3
- package/esm/document/common/document-init-context.js +0 -5
- package/esm/document/common/document-node.js +2 -3
- package/esm/document/data-type/api-field.js +10 -19
- package/esm/document/data-type/complex-type-base.js +16 -14
- package/esm/document/data-type/complex-type.js +14 -7
- package/esm/document/data-type/data-type.js +12 -14
- package/esm/document/data-type/enum-type.js +12 -9
- package/esm/document/data-type/extended-types/field-path.type.js +7 -7
- package/esm/document/data-type/extended-types/filter.type.js +5 -5
- package/esm/document/data-type/mapped-type.js +10 -4
- package/esm/document/data-type/mixin-type.js +13 -4
- package/esm/document/data-type/simple-type.js +11 -16
- package/esm/document/decorators/api-field-decorator.js +36 -2
- package/esm/document/decorators/simple-type.decorator.js +2 -0
- package/esm/document/factory/api-document.factory.js +0 -1
- package/esm/document/utils/test-scope-match.js +10 -0
- package/esm/filter/filter-rules.js +6 -7
- package/package.json +1 -1
- package/types/document/api-document.d.ts +2 -3
- package/types/document/common/document-init-context.d.ts +0 -1
- package/types/document/common/document-node.d.ts +8 -8
- package/types/document/data-type/api-field.d.ts +40 -27
- package/types/document/data-type/complex-type-base.d.ts +4 -2
- package/types/document/data-type/complex-type.d.ts +1 -0
- package/types/document/data-type/data-type.d.ts +6 -5
- package/types/document/data-type/enum-type.d.ts +1 -0
- package/types/document/data-type/extended-types/field-path.type.d.ts +2 -2
- package/types/document/data-type/mapped-type.d.ts +1 -0
- package/types/document/data-type/mixin-type.d.ts +1 -0
- package/types/document/data-type/simple-type.d.ts +1 -1
- package/types/document/decorators/api-field-decorator.d.ts +5 -3
- package/types/document/decorators/simple-type.decorator.d.ts +34 -0
- package/types/document/factory/api-document.factory.d.ts +1 -1
- package/types/document/http/http-api.d.ts +1 -1
- package/types/document/utils/test-scope-match.d.ts +1 -0
- package/types/filter/filter-rules.d.ts +3 -3
|
@@ -71,7 +71,6 @@ export class ApiDocument extends DocumentElement {
|
|
|
71
71
|
url: this.url,
|
|
72
72
|
info: cloneObject(this.info, true),
|
|
73
73
|
});
|
|
74
|
-
options = options || { scopes: this.scopes };
|
|
75
74
|
if (this.references.size) {
|
|
76
75
|
let i = 0;
|
|
77
76
|
const references = {};
|
|
@@ -91,7 +90,7 @@ export class ApiDocument extends DocumentElement {
|
|
|
91
90
|
if (this.types.size) {
|
|
92
91
|
out.types = {};
|
|
93
92
|
for (const v of this.types.values()) {
|
|
94
|
-
if (!v.inScope(options
|
|
93
|
+
if (options?.scope && !v.inScope(options?.scope))
|
|
95
94
|
continue;
|
|
96
95
|
out.types[v.name] = v.toJSON(options);
|
|
97
96
|
}
|
|
@@ -110,7 +109,7 @@ export class ApiDocument extends DocumentElement {
|
|
|
110
109
|
}
|
|
111
110
|
_findDataType(nameOrCtor, scope, visitedRefs) {
|
|
112
111
|
let result = this.types.get(nameOrCtor);
|
|
113
|
-
if (result && result.inScope(scope))
|
|
112
|
+
if (result && (!scope || result.inScope(scope)))
|
|
114
113
|
return result;
|
|
115
114
|
if (!this.references.size)
|
|
116
115
|
return;
|
|
@@ -6,11 +6,6 @@ export class DocumentInitContext {
|
|
|
6
6
|
this.showErrorDetails = true;
|
|
7
7
|
this.maxErrors = options?.maxErrors || 0;
|
|
8
8
|
this.error.message = '';
|
|
9
|
-
this.scopes = options?.scopes
|
|
10
|
-
? Array.isArray(options.scopes)
|
|
11
|
-
? options?.scopes
|
|
12
|
-
: [options?.scopes]
|
|
13
|
-
: undefined;
|
|
14
9
|
}
|
|
15
10
|
addError(error) {
|
|
16
11
|
if (!this.error.details.length) {
|
|
@@ -22,9 +22,8 @@ export class DocumentNode {
|
|
|
22
22
|
return !!this.findDataType(nameOrCtor, scope);
|
|
23
23
|
}
|
|
24
24
|
findDataType(nameOrCtor, scope) {
|
|
25
|
-
scope = scope || this.getDocument().scopes;
|
|
26
25
|
const dt = this[kDataTypeMap]?.get(nameOrCtor);
|
|
27
|
-
if (dt && dt.inScope(scope))
|
|
26
|
+
if (dt && (!scope || dt.inScope(scope)))
|
|
28
27
|
return dt;
|
|
29
28
|
return this.parent
|
|
30
29
|
? this.parent.findDataType(nameOrCtor, scope)
|
|
@@ -35,7 +34,7 @@ export class DocumentNode {
|
|
|
35
34
|
*/
|
|
36
35
|
getDataType(nameOrCtor, scope) {
|
|
37
36
|
const dt = this.findDataType(nameOrCtor);
|
|
38
|
-
if (dt && dt.inScope(scope))
|
|
37
|
+
if (dt && (!scope || dt.inScope(scope)))
|
|
39
38
|
return dt;
|
|
40
39
|
let name = '';
|
|
41
40
|
if (typeof nameOrCtor === 'function') {
|
|
@@ -2,10 +2,10 @@ import { omitUndefined } from '@jsopen/objects';
|
|
|
2
2
|
import { asMutable } from 'ts-gems';
|
|
3
3
|
import { DocumentElement } from '../common/document-element.js';
|
|
4
4
|
import { DECORATOR } from '../constants.js';
|
|
5
|
-
import {
|
|
5
|
+
import { ApiFieldDecoratorFactory } from '../decorators/api-field-decorator.js';
|
|
6
|
+
import { testScopeMatch } from '../utils/test-scope-match.js';
|
|
6
7
|
import { ComplexTypeBase } from './complex-type-base.js';
|
|
7
8
|
/**
|
|
8
|
-
* @constructor ApiField
|
|
9
9
|
* @decorator ApiField
|
|
10
10
|
*/
|
|
11
11
|
export const ApiField = function (...args) {
|
|
@@ -25,7 +25,7 @@ export const ApiField = function (...args) {
|
|
|
25
25
|
throw new Error('Field origin should be one of ComplexType, MappedType or MixinType');
|
|
26
26
|
}
|
|
27
27
|
_this.origin = origin;
|
|
28
|
-
_this.
|
|
28
|
+
_this.scopePattern = initArgs.scopePattern;
|
|
29
29
|
_this.type = initArgs.type || owner.node.getDataType('any');
|
|
30
30
|
_this.description = initArgs.description;
|
|
31
31
|
_this.isArray = initArgs.isArray;
|
|
@@ -41,22 +41,13 @@ export const ApiField = function (...args) {
|
|
|
41
41
|
_this.examples = initArgs.examples;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* The ApiFieldClass represents a descriptive metadata structure for API fields,
|
|
45
|
+
* supporting features like data type definition, scoping, localization, and constraints.
|
|
46
|
+
* This class extends DocumentElement, inheriting base document structure capabilities.
|
|
46
47
|
*/
|
|
47
48
|
class ApiFieldClass extends DocumentElement {
|
|
48
|
-
inScope(
|
|
49
|
-
|
|
50
|
-
return true;
|
|
51
|
-
/** this.scope should match all required scopes */
|
|
52
|
-
scopes = Array.isArray(scopes) ? scopes : [scopes];
|
|
53
|
-
for (const scope of scopes) {
|
|
54
|
-
if (!this.scopes.some(s => {
|
|
55
|
-
return typeof s === 'string' ? scope === s : s.test(scope);
|
|
56
|
-
}))
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
return true;
|
|
49
|
+
inScope(scope) {
|
|
50
|
+
return testScopeMatch(scope, this.scopePattern);
|
|
60
51
|
}
|
|
61
52
|
toJSON(options) {
|
|
62
53
|
const typeName = this.type
|
|
@@ -80,5 +71,5 @@ class ApiFieldClass extends DocumentElement {
|
|
|
80
71
|
}
|
|
81
72
|
}
|
|
82
73
|
ApiField.prototype = ApiFieldClass.prototype;
|
|
83
|
-
Object.assign(ApiField,
|
|
84
|
-
ApiField[DECORATOR] =
|
|
74
|
+
Object.assign(ApiField, ApiFieldDecoratorFactory);
|
|
75
|
+
ApiField[DECORATOR] = ApiFieldDecoratorFactory;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { asMutable } from 'ts-gems';
|
|
2
2
|
import { validator, vg } from 'valgen';
|
|
3
3
|
import { parseFieldsProjection, ResponsiveMap, } from '../../helpers/index.js';
|
|
4
|
-
import { translate } from '../../i18n/index.js';
|
|
5
4
|
import { DataType } from './data-type.js';
|
|
6
5
|
export const FIELD_PATH_PATTERN = /^([+-])?([a-z$_][\w.]*)$/i;
|
|
7
6
|
/**
|
|
@@ -24,21 +23,29 @@ class ComplexTypeBaseClass extends DataType {
|
|
|
24
23
|
/**
|
|
25
24
|
*
|
|
26
25
|
*/
|
|
27
|
-
findField(nameOrPath) {
|
|
26
|
+
findField(nameOrPath, scope) {
|
|
28
27
|
if (nameOrPath.includes('.')) {
|
|
29
|
-
const fieldPath = this.parseFieldPath(nameOrPath);
|
|
28
|
+
const fieldPath = this.parseFieldPath(nameOrPath, { scope });
|
|
29
|
+
if (fieldPath.length === 0)
|
|
30
|
+
throw new Error(`Field "${nameOrPath}" does not exist in scope "${scope}"`);
|
|
30
31
|
const lastItem = fieldPath.pop();
|
|
31
32
|
return lastItem?.field;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
const field = this.fields.get(nameOrPath);
|
|
35
|
+
if (field && scope && !field.inScope(scope))
|
|
36
|
+
return;
|
|
37
|
+
return field;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
*
|
|
37
41
|
*/
|
|
38
|
-
getField(nameOrPath) {
|
|
42
|
+
getField(nameOrPath, scope) {
|
|
39
43
|
const field = this.findField(nameOrPath);
|
|
40
|
-
if (!field)
|
|
41
|
-
throw new Error(
|
|
44
|
+
if (field && scope && !field.inScope(scope))
|
|
45
|
+
throw new Error(`Field "${nameOrPath}" does not exist in scope "${scope}"`);
|
|
46
|
+
if (!field) {
|
|
47
|
+
throw new Error(`Field (${nameOrPath}) does not exist`);
|
|
48
|
+
}
|
|
42
49
|
return field;
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
@@ -69,7 +76,7 @@ class ComplexTypeBaseClass extends DataType {
|
|
|
69
76
|
}
|
|
70
77
|
if (dataType) {
|
|
71
78
|
if (dataType instanceof ComplexTypeBase) {
|
|
72
|
-
field = dataType.
|
|
79
|
+
field = dataType.getField(item.fieldName, options?.scope);
|
|
73
80
|
if (field) {
|
|
74
81
|
item.fieldName = field.name;
|
|
75
82
|
item.field = field;
|
|
@@ -146,17 +153,12 @@ class ComplexTypeBaseClass extends DataType {
|
|
|
146
153
|
const schema = {};
|
|
147
154
|
const { currentPath, projection } = context;
|
|
148
155
|
const pickList = !!(projection && Object.values(projection).find(p => !p.sign));
|
|
149
|
-
const scope = context.scope
|
|
150
|
-
? Array.isArray(context.scope)
|
|
151
|
-
? context.scope
|
|
152
|
-
: [context.scope]
|
|
153
|
-
: undefined;
|
|
154
156
|
// Process fields
|
|
155
157
|
let fieldName;
|
|
156
158
|
for (const field of this.fields.values()) {
|
|
157
159
|
if (
|
|
158
160
|
/** Ignore field if required scope(s) do not match field scopes */
|
|
159
|
-
(scope && !field.inScope(scope)) ||
|
|
161
|
+
(context.scope && !field.inScope(context.scope)) ||
|
|
160
162
|
/** Ignore field if readonly and ignoreReadonlyFields option true */
|
|
161
163
|
(context.ignoreReadonlyFields && field.readonly) ||
|
|
162
164
|
/** Ignore field if writeonly and ignoreWriteonlyFields option true */
|
|
@@ -69,21 +69,19 @@ class ComplexTypeClass extends ComplexTypeBase {
|
|
|
69
69
|
return !!this.base?.extendsFrom(baseType);
|
|
70
70
|
}
|
|
71
71
|
toJSON(options) {
|
|
72
|
+
const superJson = super.toJSON(options);
|
|
72
73
|
const baseName = this.base
|
|
73
74
|
? this.node.getDataTypeNameWithNs(this.base)
|
|
74
75
|
: undefined;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
78
|
-
const out = omitUndefined({
|
|
79
|
-
...ComplexTypeBase.prototype.toJSON.call(this),
|
|
76
|
+
const out = {
|
|
77
|
+
...superJson,
|
|
80
78
|
kind: this.kind,
|
|
81
79
|
base: this.base
|
|
82
80
|
? baseName
|
|
83
81
|
? baseName
|
|
84
82
|
: this.base.toJSON(options)
|
|
85
83
|
: undefined,
|
|
86
|
-
}
|
|
84
|
+
};
|
|
87
85
|
if (this.additionalFields) {
|
|
88
86
|
if (this.additionalFields instanceof DataType) {
|
|
89
87
|
const typeName = this.node.getDataTypeNameWithNs(this.additionalFields);
|
|
@@ -98,7 +96,8 @@ class ComplexTypeClass extends ComplexTypeBase {
|
|
|
98
96
|
const fields = {};
|
|
99
97
|
let i = 0;
|
|
100
98
|
for (const field of this.fields.values()) {
|
|
101
|
-
if (field.origin === this &&
|
|
99
|
+
if (field.origin === this &&
|
|
100
|
+
(!options?.scope || field.inScope(options?.scope))) {
|
|
102
101
|
fields[field.name] = field.toJSON(options);
|
|
103
102
|
i++;
|
|
104
103
|
}
|
|
@@ -108,6 +107,14 @@ class ComplexTypeClass extends ComplexTypeBase {
|
|
|
108
107
|
}
|
|
109
108
|
return omitUndefined(out);
|
|
110
109
|
}
|
|
110
|
+
_locateBase(callback) {
|
|
111
|
+
if (!this.base)
|
|
112
|
+
return;
|
|
113
|
+
if (callback(this.base))
|
|
114
|
+
return this.base;
|
|
115
|
+
if (this.base._locateBase)
|
|
116
|
+
return this.base._locateBase(callback);
|
|
117
|
+
}
|
|
111
118
|
}
|
|
112
119
|
ComplexType.prototype = ComplexTypeClass.prototype;
|
|
113
120
|
Object.assign(ComplexType, ComplexTypeDecorator);
|
|
@@ -3,6 +3,7 @@ import { asMutable } from 'ts-gems';
|
|
|
3
3
|
import { DocumentElement } from '../common/document-element.js';
|
|
4
4
|
import { CLASS_NAME_PATTERN } from '../constants.js';
|
|
5
5
|
import { colorFgMagenta, colorFgYellow, colorReset, nodeInspectCustom, } from '../utils/inspect.util.js';
|
|
6
|
+
import { testScopeMatch } from '../utils/test-scope-match.js';
|
|
6
7
|
/**
|
|
7
8
|
* DataType constructor
|
|
8
9
|
*/
|
|
@@ -17,7 +18,7 @@ context) {
|
|
|
17
18
|
DocumentElement.call(this, owner);
|
|
18
19
|
const _this = asMutable(this);
|
|
19
20
|
_this.kind = initArgs.kind;
|
|
20
|
-
_this.
|
|
21
|
+
_this.scopePattern = initArgs.scopePattern;
|
|
21
22
|
_this.name = initArgs.name;
|
|
22
23
|
_this.description = initArgs.description;
|
|
23
24
|
_this.abstract = initArgs.abstract;
|
|
@@ -31,21 +32,18 @@ class DataTypeClass extends DocumentElement {
|
|
|
31
32
|
get embedded() {
|
|
32
33
|
return !this.name;
|
|
33
34
|
}
|
|
34
|
-
inScope(
|
|
35
|
-
|
|
36
|
-
return true;
|
|
37
|
-
/** this.scope should match all required scopes */
|
|
38
|
-
scopes = Array.isArray(scopes) ? scopes : [scopes];
|
|
39
|
-
for (const scope of scopes) {
|
|
40
|
-
if (!this.scopes.some(s => {
|
|
41
|
-
return typeof s === 'string' ? scope === s : s.test(scope);
|
|
42
|
-
}))
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
35
|
+
inScope(scope) {
|
|
36
|
+
return testScopeMatch(scope, this.scopePattern);
|
|
46
37
|
}
|
|
47
|
-
// eslint-disable-next-line
|
|
48
38
|
toJSON(options) {
|
|
39
|
+
if (options?.scope) {
|
|
40
|
+
/** Locate base model which is not available for given scope */
|
|
41
|
+
const outOfScope = this._locateBase(dt => !dt.inScope(options.scope));
|
|
42
|
+
if (outOfScope) {
|
|
43
|
+
const baseName = this.node.getDataTypeNameWithNs(outOfScope);
|
|
44
|
+
throw new TypeError(`"${baseName}" model is not available for "${options.scope}" scope`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
49
47
|
return omitUndefined({
|
|
50
48
|
kind: this.kind,
|
|
51
49
|
description: this.description,
|
|
@@ -49,22 +49,25 @@ class EnumTypeClass extends DataType {
|
|
|
49
49
|
return vg.isEnum(Object.keys(this.attributes));
|
|
50
50
|
}
|
|
51
51
|
toJSON(options) {
|
|
52
|
+
const superJson = super.toJSON(options);
|
|
52
53
|
const baseName = this.base
|
|
53
54
|
? this.node.getDataTypeNameWithNs(this.base)
|
|
54
55
|
: undefined;
|
|
55
|
-
if (options?.scopes && !this.base?.inScope(options?.scopes))
|
|
56
|
-
throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
|
|
57
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
58
56
|
return omitUndefined({
|
|
59
|
-
...
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
? baseName
|
|
63
|
-
: this.base.toJSON(options)
|
|
64
|
-
: undefined,
|
|
57
|
+
...superJson,
|
|
58
|
+
kind: this.kind,
|
|
59
|
+
base: baseName,
|
|
65
60
|
attributes: cloneObject(this.ownAttributes),
|
|
66
61
|
});
|
|
67
62
|
}
|
|
63
|
+
_locateBase(callback) {
|
|
64
|
+
if (!this.base)
|
|
65
|
+
return;
|
|
66
|
+
if (callback(this.base))
|
|
67
|
+
return this.base;
|
|
68
|
+
if (this.base._locateBase)
|
|
69
|
+
return this.base._locateBase(callback);
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
EnumType.prototype = EnumTypeClass.prototype;
|
|
70
73
|
Object.assign(EnumType, EnumTypeClass);
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
2
|
import { toString, validator, vg } from 'valgen';
|
|
3
3
|
import { DECODER, ENCODER } from '../../constants.js';
|
|
4
|
+
import { DataType } from '../data-type.js';
|
|
4
5
|
import { SimpleType } from '../simple-type.js';
|
|
5
6
|
let FieldPathType = class FieldPathType {
|
|
6
7
|
constructor(attributes) {
|
|
7
8
|
if (attributes)
|
|
8
9
|
Object.assign(this, attributes);
|
|
9
10
|
}
|
|
10
|
-
[DECODER](properties, element) {
|
|
11
|
+
[DECODER](properties, element, scope) {
|
|
11
12
|
const dataType = properties.dataType
|
|
12
13
|
? element.node.getComplexType(properties.dataType)
|
|
13
14
|
: element.node.getComplexType('object');
|
|
14
15
|
const allowSigns = properties.allowSigns;
|
|
15
|
-
const decodeFieldPath = validator('decodeFieldPath', (input) => dataType.normalizeFieldPath(input, { allowSigns }));
|
|
16
|
+
const decodeFieldPath = validator('decodeFieldPath', (input) => dataType.normalizeFieldPath(input, { allowSigns, scope }));
|
|
16
17
|
return vg.pipe([toString, decodeFieldPath]);
|
|
17
18
|
}
|
|
18
|
-
[ENCODER](properties, element) {
|
|
19
|
-
return this[DECODER](properties, element);
|
|
19
|
+
[ENCODER](properties, element, scope) {
|
|
20
|
+
return this[DECODER](properties, element, scope);
|
|
20
21
|
}
|
|
21
22
|
toJSON(properties, element, options) {
|
|
22
23
|
const dataType = properties.dataType
|
|
23
24
|
? element.node.getComplexType(properties.dataType)
|
|
24
25
|
: element.node.getComplexType('object');
|
|
26
|
+
/** Test scope */
|
|
27
|
+
DataType.prototype.toJSON.call(dataType, options);
|
|
25
28
|
const typeName = dataType
|
|
26
29
|
? element.node.getDataTypeNameWithNs(dataType)
|
|
27
30
|
: undefined;
|
|
28
|
-
if (options?.scopes && !dataType.inScope(options?.scopes))
|
|
29
|
-
throw new TypeError(`Data type ${typeName ? '(' + typeName + ')' : ''} ` +
|
|
30
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
31
31
|
return {
|
|
32
32
|
dataType: typeName ? typeName : dataType.toJSON(options),
|
|
33
33
|
allowSigns: properties.allowSigns,
|
|
@@ -3,6 +3,7 @@ import { validator } from 'valgen';
|
|
|
3
3
|
import { FilterRules } from '../../../filter/filter-rules.js';
|
|
4
4
|
import { OpraFilter } from '../../../filter/index.js';
|
|
5
5
|
import { DECODER, ENCODER } from '../../constants.js';
|
|
6
|
+
import { DataType } from '../data-type.js';
|
|
6
7
|
import { SimpleType } from '../simple-type.js';
|
|
7
8
|
let FilterType = class FilterType {
|
|
8
9
|
constructor(attributes) {
|
|
@@ -25,12 +26,11 @@ let FilterType = class FilterType {
|
|
|
25
26
|
const dataType = properties.dataType
|
|
26
27
|
? element.node.getComplexType(properties.dataType)
|
|
27
28
|
: element.node.getComplexType('object');
|
|
29
|
+
/** Test scope */
|
|
30
|
+
DataType.prototype.toJSON.call(dataType, options);
|
|
28
31
|
const typeName = dataType
|
|
29
32
|
? element.node.getDataTypeNameWithNs(dataType)
|
|
30
33
|
: undefined;
|
|
31
|
-
if (options?.scopes && !dataType.inScope(options?.scopes))
|
|
32
|
-
throw new TypeError(`Data type ${typeName ? '(' + typeName + ')' : ''} ` +
|
|
33
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
34
34
|
return {
|
|
35
35
|
dataType: typeName ? typeName : dataType.toJSON(options),
|
|
36
36
|
rules: properties.rules,
|
|
@@ -62,12 +62,12 @@ FilterType = __decorate([
|
|
|
62
62
|
__metadata("design:paramtypes", [Object])
|
|
63
63
|
], FilterType);
|
|
64
64
|
export { FilterType };
|
|
65
|
-
const decodeFilter = (dataType, rules,
|
|
65
|
+
const decodeFilter = (dataType, rules, scope) => validator('decodeFilter', (input, context, _this) => {
|
|
66
66
|
if (typeof input === 'string') {
|
|
67
67
|
try {
|
|
68
68
|
const filter = OpraFilter.parse(input);
|
|
69
69
|
if (rules)
|
|
70
|
-
return rules.normalizeFilter(filter, dataType,
|
|
70
|
+
return rules.normalizeFilter(filter, dataType, scope);
|
|
71
71
|
return filter;
|
|
72
72
|
}
|
|
73
73
|
catch (e) {
|
|
@@ -86,14 +86,12 @@ class MappedTypeClass extends ComplexTypeBase {
|
|
|
86
86
|
return !!this.base?.extendsFrom(baseType);
|
|
87
87
|
}
|
|
88
88
|
toJSON(options) {
|
|
89
|
+
const superJson = super.toJSON(options);
|
|
89
90
|
const baseName = this.base
|
|
90
91
|
? this.node.getDataTypeNameWithNs(this.base)
|
|
91
92
|
: undefined;
|
|
92
|
-
if (options?.scopes && !this.base?.inScope(options?.scopes))
|
|
93
|
-
throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
|
|
94
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
95
93
|
return omitUndefined({
|
|
96
|
-
...
|
|
94
|
+
...superJson,
|
|
97
95
|
base: baseName ? baseName : this.base.toJSON(options),
|
|
98
96
|
kind: this.kind,
|
|
99
97
|
pick: this.pick,
|
|
@@ -102,6 +100,14 @@ class MappedTypeClass extends ComplexTypeBase {
|
|
|
102
100
|
required: this.required,
|
|
103
101
|
});
|
|
104
102
|
}
|
|
103
|
+
_locateBase(callback) {
|
|
104
|
+
if (!this.base)
|
|
105
|
+
return;
|
|
106
|
+
if (callback(this.base))
|
|
107
|
+
return this.base;
|
|
108
|
+
if (this.base._locateBase)
|
|
109
|
+
return this.base._locateBase(callback);
|
|
110
|
+
}
|
|
105
111
|
}
|
|
106
112
|
MappedType.prototype = MappedTypeClass.prototype;
|
|
107
113
|
MappedType._applyMixin = () => undefined;
|
|
@@ -55,18 +55,27 @@ class MixinTypeClass extends ComplexTypeBase {
|
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
57
57
|
toJSON(options) {
|
|
58
|
+
const superJson = super.toJSON(options);
|
|
58
59
|
return omitUndefined({
|
|
59
|
-
...
|
|
60
|
+
...superJson,
|
|
60
61
|
kind: this.kind,
|
|
61
62
|
types: this.types.map(base => {
|
|
62
63
|
const baseName = this.node.getDataTypeNameWithNs(base);
|
|
63
|
-
if (options?.scopes && !base?.inScope(options?.scopes))
|
|
64
|
-
throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
|
|
65
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
66
64
|
return baseName ? baseName : base.toJSON(options);
|
|
67
65
|
}),
|
|
68
66
|
});
|
|
69
67
|
}
|
|
68
|
+
_locateBase(callback) {
|
|
69
|
+
for (const t of this.types) {
|
|
70
|
+
if (callback(t))
|
|
71
|
+
return t;
|
|
72
|
+
if (t._locateBase) {
|
|
73
|
+
const x = t._locateBase(callback);
|
|
74
|
+
if (x)
|
|
75
|
+
return x;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
MixinType.prototype = MixinTypeClass.prototype;
|
|
72
81
|
MixinType[DECORATOR] = MixinTypeFactory;
|
|
@@ -67,7 +67,7 @@ class SimpleTypeClass extends DataType {
|
|
|
67
67
|
let t = this;
|
|
68
68
|
while (t) {
|
|
69
69
|
if (t._generateDecoder)
|
|
70
|
-
return t._generateDecoder(prop, options?.documentElement || this.owner);
|
|
70
|
+
return t._generateDecoder(prop, options?.documentElement || this.owner, options?.scope);
|
|
71
71
|
t = this.base;
|
|
72
72
|
}
|
|
73
73
|
return isAny;
|
|
@@ -75,12 +75,16 @@ class SimpleTypeClass extends DataType {
|
|
|
75
75
|
let t = this;
|
|
76
76
|
while (t) {
|
|
77
77
|
if (t._generateEncoder)
|
|
78
|
-
return t._generateEncoder(prop, options?.documentElement || this.owner);
|
|
78
|
+
return t._generateEncoder(prop, options?.documentElement || this.owner, options?.scope);
|
|
79
79
|
t = this.base;
|
|
80
80
|
}
|
|
81
81
|
return isAny;
|
|
82
82
|
}
|
|
83
83
|
toJSON(options) {
|
|
84
|
+
const superJson = super.toJSON(options);
|
|
85
|
+
const baseName = this.base
|
|
86
|
+
? this.node.getDataTypeNameWithNs(this.base)
|
|
87
|
+
: undefined;
|
|
84
88
|
const attributes = omitUndefined(this.ownAttributes);
|
|
85
89
|
let properties;
|
|
86
90
|
if (this.properties && typeof this.properties.toJSON === 'function') {
|
|
@@ -88,22 +92,13 @@ class SimpleTypeClass extends DataType {
|
|
|
88
92
|
}
|
|
89
93
|
else
|
|
90
94
|
properties = this.properties ? cloneObject(this.properties) : {};
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
:
|
|
94
|
-
|
|
95
|
-
throw new TypeError(`Base type ${baseName ? '(' + baseName + ')' : ''} of ${this.name ? +this.name : 'embedded'} type ` +
|
|
96
|
-
`is not in required scope(s) [${options.scopes}`);
|
|
97
|
-
const out = omitUndefined({
|
|
98
|
-
...DataType.prototype.toJSON.apply(this),
|
|
99
|
-
base: this.base
|
|
100
|
-
? baseName
|
|
101
|
-
? baseName
|
|
102
|
-
: this.base.toJSON(options)
|
|
103
|
-
: undefined,
|
|
95
|
+
const out = {
|
|
96
|
+
...superJson,
|
|
97
|
+
kind: this.kind,
|
|
98
|
+
base: baseName,
|
|
104
99
|
attributes: attributes && Object.keys(attributes).length ? attributes : undefined,
|
|
105
100
|
properties: Object.keys(properties).length ? properties : undefined,
|
|
106
|
-
}
|
|
101
|
+
};
|
|
107
102
|
if (Object.keys(this.ownNameMappings).length)
|
|
108
103
|
out.nameMappings = { ...this.ownNameMappings };
|
|
109
104
|
return omitUndefined(out, true);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// import { omitUndefined } from '@jsopen/objects';
|
|
2
|
+
// import { StrictOmit } from 'ts-gems';
|
|
1
3
|
import { OpraSchema } from '../../schema/index.js';
|
|
2
4
|
import { DATATYPE_METADATA } from '../constants.js';
|
|
3
|
-
export function
|
|
4
|
-
|
|
5
|
+
export function ApiFieldDecoratorFactory(options) {
|
|
6
|
+
const decoratorChain = [];
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
const decorator = function (target, propertyKey) {
|
|
5
11
|
if (typeof propertyKey !== 'string')
|
|
6
12
|
throw new TypeError(`Symbol properties can't be used as a field`);
|
|
7
13
|
const metadata = Reflect.getOwnMetadata(DATATYPE_METADATA, target.constructor) ||
|
|
@@ -11,6 +17,11 @@ export function ApiFieldDecorator(options) {
|
|
|
11
17
|
const designType = Reflect.getMetadata('design:type', target, propertyKey);
|
|
12
18
|
const elemMeta = (metadata.fields[propertyKey] = {
|
|
13
19
|
...options,
|
|
20
|
+
scopePattern: options?.scopePattern
|
|
21
|
+
? Array.isArray(options.scopePattern)
|
|
22
|
+
? options.scopePattern
|
|
23
|
+
: [options.scopePattern]
|
|
24
|
+
: undefined,
|
|
14
25
|
});
|
|
15
26
|
if (designType === Array) {
|
|
16
27
|
elemMeta.isArray = true;
|
|
@@ -18,5 +29,28 @@ export function ApiFieldDecorator(options) {
|
|
|
18
29
|
else
|
|
19
30
|
elemMeta.type = elemMeta.type || designType;
|
|
20
31
|
Reflect.defineMetadata(DATATYPE_METADATA, metadata, target.constructor);
|
|
32
|
+
for (const fn of decoratorChain)
|
|
33
|
+
fn(metadata, target);
|
|
21
34
|
};
|
|
35
|
+
// /**
|
|
36
|
+
// *
|
|
37
|
+
// */
|
|
38
|
+
// decorator.Override = (
|
|
39
|
+
// scopes: (string | RegExp) | (string | RegExp)[],
|
|
40
|
+
// opts: StrictOmit<ApiField.Options, 'isArray' | 'type' | 'scopePattern'>,
|
|
41
|
+
// ): any => {
|
|
42
|
+
// decoratorChain.push((meta: ApiField.Metadata): void => {
|
|
43
|
+
// meta.overrides = meta.overrides || [];
|
|
44
|
+
// meta.overrides.push(
|
|
45
|
+
// omitUndefined({
|
|
46
|
+
// ...opts,
|
|
47
|
+
// scopes: Array.isArray(scopes) ? scopes : [scopes],
|
|
48
|
+
// type: undefined,
|
|
49
|
+
// isArray: undefined,
|
|
50
|
+
// }),
|
|
51
|
+
// );
|
|
52
|
+
// });
|
|
53
|
+
// return decorator;
|
|
54
|
+
// };
|
|
55
|
+
return decorator;
|
|
22
56
|
}
|
|
@@ -24,6 +24,8 @@ export function SimpleTypeDecoratorFactory(options) {
|
|
|
24
24
|
metadata.kind = OpraSchema.SimpleType.Kind;
|
|
25
25
|
metadata.name = name;
|
|
26
26
|
Reflect.defineMetadata(DATATYPE_METADATA, metadata, target);
|
|
27
|
+
for (const fn of decoratorChain)
|
|
28
|
+
fn(metadata, target);
|
|
27
29
|
};
|
|
28
30
|
decorator.Example = (value, description) => {
|
|
29
31
|
decoratorChain.push((meta) => {
|
|
@@ -27,7 +27,6 @@ export class ApiDocumentFactory {
|
|
|
27
27
|
: new DocumentInitContext(options);
|
|
28
28
|
try {
|
|
29
29
|
const document = new ApiDocument();
|
|
30
|
-
document.scopes = context.scopes;
|
|
31
30
|
await factory.initDocument(document, context, schemaOrUrl);
|
|
32
31
|
if (context.error.details.length)
|
|
33
32
|
throw context.error;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function testScopeMatch(scope, pattern) {
|
|
2
|
+
if (!(scope && pattern))
|
|
3
|
+
return true;
|
|
4
|
+
if (Array.isArray(pattern)) {
|
|
5
|
+
return pattern.some(x => {
|
|
6
|
+
return typeof x === 'string' ? scope === x : x.test(scope);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
return typeof pattern === 'string' ? scope === pattern : pattern.test(scope);
|
|
10
|
+
}
|
|
@@ -29,12 +29,11 @@ export class FilterRules {
|
|
|
29
29
|
operators,
|
|
30
30
|
}));
|
|
31
31
|
}
|
|
32
|
-
normalizeFilter(filter, currentType,
|
|
32
|
+
normalizeFilter(filter, currentType, scope) {
|
|
33
33
|
const ast = typeof filter === 'string' ? parse(filter) : filter;
|
|
34
|
-
|
|
35
|
-
return this.normalizeFilterAst(ast, [], currentType, doc?.scopes);
|
|
34
|
+
return this.normalizeFilterAst(ast, [], currentType, scope);
|
|
36
35
|
}
|
|
37
|
-
normalizeFilterAst(ast, stack, currentType,
|
|
36
|
+
normalizeFilterAst(ast, stack, currentType, scope) {
|
|
38
37
|
if (ast instanceof ComparisonExpression) {
|
|
39
38
|
stack.push(ast);
|
|
40
39
|
this.normalizeFilterAst(ast.left, stack, currentType);
|
|
@@ -97,8 +96,8 @@ export class FilterRules {
|
|
|
97
96
|
return ast;
|
|
98
97
|
}
|
|
99
98
|
if (ast instanceof QualifiedIdentifier && currentType) {
|
|
100
|
-
ast.value = currentType.normalizeFieldPath(ast.value);
|
|
101
|
-
ast.field = currentType.getField(ast.value);
|
|
99
|
+
ast.value = currentType.normalizeFieldPath(ast.value, { scope });
|
|
100
|
+
ast.field = currentType.getField(ast.value, scope);
|
|
102
101
|
ast.dataType = ast.field.type;
|
|
103
102
|
return ast;
|
|
104
103
|
}
|
|
@@ -126,7 +125,7 @@ export class FilterRules {
|
|
|
126
125
|
decoder = this._decoderCache.get(comp.left.field);
|
|
127
126
|
if (!decoder) {
|
|
128
127
|
decoder = comp.left.field.type.generateCodec('decode', {
|
|
129
|
-
scope
|
|
128
|
+
scope,
|
|
130
129
|
projection: '*',
|
|
131
130
|
ignoreWriteonlyFields: true,
|
|
132
131
|
coerce: true,
|