@api-client/core 0.19.9 → 0.19.10
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/Testing.md +1 -1
- package/build/src/decorators/observed.d.ts.map +1 -1
- package/build/src/decorators/observed.js +91 -0
- package/build/src/decorators/observed.js.map +1 -1
- package/build/src/modeling/ApiModel.d.ts +21 -7
- package/build/src/modeling/ApiModel.d.ts.map +1 -1
- package/build/src/modeling/ApiModel.js +70 -29
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/DomainValidation.d.ts +1 -1
- package/build/src/modeling/DomainValidation.d.ts.map +1 -1
- package/build/src/modeling/DomainValidation.js.map +1 -1
- package/build/src/modeling/ExposedEntity.d.ts +14 -0
- package/build/src/modeling/ExposedEntity.d.ts.map +1 -1
- package/build/src/modeling/ExposedEntity.js +59 -6
- package/build/src/modeling/ExposedEntity.js.map +1 -1
- package/build/src/modeling/actions/Action.d.ts +11 -1
- package/build/src/modeling/actions/Action.d.ts.map +1 -1
- package/build/src/modeling/actions/Action.js +21 -3
- package/build/src/modeling/actions/Action.js.map +1 -1
- package/build/src/modeling/actions/CreateAction.d.ts +2 -1
- package/build/src/modeling/actions/CreateAction.d.ts.map +1 -1
- package/build/src/modeling/actions/CreateAction.js +2 -2
- package/build/src/modeling/actions/CreateAction.js.map +1 -1
- package/build/src/modeling/actions/DeleteAction.d.ts +2 -1
- package/build/src/modeling/actions/DeleteAction.d.ts.map +1 -1
- package/build/src/modeling/actions/DeleteAction.js +2 -2
- package/build/src/modeling/actions/DeleteAction.js.map +1 -1
- package/build/src/modeling/actions/ListAction.d.ts +2 -1
- package/build/src/modeling/actions/ListAction.d.ts.map +1 -1
- package/build/src/modeling/actions/ListAction.js +2 -2
- package/build/src/modeling/actions/ListAction.js.map +1 -1
- package/build/src/modeling/actions/ReadAction.d.ts +2 -1
- package/build/src/modeling/actions/ReadAction.d.ts.map +1 -1
- package/build/src/modeling/actions/ReadAction.js +2 -2
- package/build/src/modeling/actions/ReadAction.js.map +1 -1
- package/build/src/modeling/actions/SearchAction.d.ts +2 -1
- package/build/src/modeling/actions/SearchAction.d.ts.map +1 -1
- package/build/src/modeling/actions/SearchAction.js +2 -2
- package/build/src/modeling/actions/SearchAction.js.map +1 -1
- package/build/src/modeling/actions/UpdateAction.d.ts +2 -1
- package/build/src/modeling/actions/UpdateAction.d.ts.map +1 -1
- package/build/src/modeling/actions/UpdateAction.js +2 -2
- package/build/src/modeling/actions/UpdateAction.js.map +1 -1
- package/build/src/modeling/actions/index.d.ts +2 -1
- package/build/src/modeling/actions/index.d.ts.map +1 -1
- package/build/src/modeling/actions/index.js +7 -7
- package/build/src/modeling/actions/index.js.map +1 -1
- package/build/src/modeling/index.d.ts +1 -0
- package/build/src/modeling/index.d.ts.map +1 -1
- package/build/src/modeling/index.js +1 -0
- package/build/src/modeling/index.js.map +1 -1
- package/build/src/modeling/types.d.ts +67 -0
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/src/modeling/validation/api_model_rules.d.ts +15 -0
- package/build/src/modeling/validation/api_model_rules.d.ts.map +1 -0
- package/build/src/modeling/validation/api_model_rules.js +599 -0
- package/build/src/modeling/validation/api_model_rules.js.map +1 -0
- package/build/src/modeling/validation/association_validation.d.ts.map +1 -1
- package/build/src/modeling/validation/association_validation.js +1 -3
- package/build/src/modeling/validation/association_validation.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +8 -8
- package/eslint.config.js +0 -1
- package/package.json +17 -122
- package/src/decorators/observed.ts +91 -0
- package/src/modeling/ApiModel.ts +73 -33
- package/src/modeling/DomainValidation.ts +1 -1
- package/src/modeling/ExposedEntity.ts +63 -9
- package/src/modeling/actions/Action.ts +25 -2
- package/src/modeling/actions/CreateAction.ts +3 -2
- package/src/modeling/actions/DeleteAction.ts +3 -2
- package/src/modeling/actions/ListAction.ts +3 -2
- package/src/modeling/actions/ReadAction.ts +3 -2
- package/src/modeling/actions/SearchAction.ts +3 -2
- package/src/modeling/actions/UpdateAction.ts +3 -2
- package/src/modeling/types.ts +70 -0
- package/src/modeling/validation/api_model_rules.ts +640 -0
- package/src/modeling/validation/api_model_validation_rules.md +58 -0
- package/src/modeling/validation/association_validation.ts +1 -3
- package/tests/unit/modeling/actions/Action.spec.ts +40 -8
- package/tests/unit/modeling/actions/CreateAction.spec.ts +5 -5
- package/tests/unit/modeling/actions/DeleteAction.spec.ts +6 -6
- package/tests/unit/modeling/actions/ListAction.spec.ts +7 -7
- package/tests/unit/modeling/actions/ReadAction.spec.ts +6 -6
- package/tests/unit/modeling/actions/SearchAction.spec.ts +6 -6
- package/tests/unit/modeling/actions/UpdateAction.spec.ts +6 -6
- package/tests/unit/modeling/api_model.spec.ts +190 -13
- package/tests/unit/modeling/api_model_expose_entity.spec.ts +43 -19
- package/tests/unit/modeling/api_model_remove_entity.spec.ts +6 -6
- package/tests/unit/modeling/exposed_entity.spec.ts +123 -3
- package/tests/unit/modeling/exposed_entity_actions.spec.ts +41 -18
- package/tests/unit/modeling/exposed_entity_setter_validation.spec.ts +1 -1
- package/tests/unit/modeling/rules/restoring_rules.spec.ts +9 -5
- package/tests/unit/modeling/validation/api_model_rules.spec.ts +324 -0
- package/tsconfig.browser.json +1 -1
- package/tsconfig.node.json +1 -1
- package/bin/test-web.ts +0 -6
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ApiModel Validation Rules
|
|
2
|
+
|
|
3
|
+
This document outlines the validation rules for the `ApiModel` schema. These rules ensure that an API model is structurally sound, secure, and ready for use or publication.
|
|
4
|
+
|
|
5
|
+
Validations are evaluated with one of three severity levels:
|
|
6
|
+
- **Error**: A blocker that makes the model invalid and must be resolved (e.g., missing required configurations or broken references).
|
|
7
|
+
- **Warning**: A non-blocker, typically indicating missing optional but recommended properties, or non-optimal configurations.
|
|
8
|
+
- **Info**: Informational messages about the model's status or suggestions for improvement.
|
|
9
|
+
|
|
10
|
+
## 1. Core Properties
|
|
11
|
+
- **`kind`** [Error]: Must be exactly the value defined by `ApiModelKind`.
|
|
12
|
+
- **`key`** [Error]: Must be a defined, non-empty string.
|
|
13
|
+
- **`info.name`** [Error]: The `info` property must be a valid structure with at least a `name` defined.
|
|
14
|
+
- **`info.description`** [Warning]: It is highly recommended to provide a description for the API model.
|
|
15
|
+
|
|
16
|
+
## 2. Domain Dependency
|
|
17
|
+
- **Domain Attachment** [Error]: The API model must have an attached `DataDomain`.
|
|
18
|
+
- **Domain Versioning** [Error]: The attached Data Domain must have a `version` defined (`domain.info.version`).
|
|
19
|
+
|
|
20
|
+
## 3. Security & Access Control
|
|
21
|
+
- **Authentication** [Error]: The `authentication` configuration is required.
|
|
22
|
+
- If the strategy is `UsernamePassword`, the `passwordKey` must be defined.
|
|
23
|
+
- **Authorization** [Error]: The `authorization` configuration is required.
|
|
24
|
+
- If the strategy is `RBAC`, the `roleKey` must be defined.
|
|
25
|
+
- **Session Configuration** [Error]: The `session` configuration is required and must meet the following criteria:
|
|
26
|
+
- **Secret**: A session encryption token (`secret`) is required.
|
|
27
|
+
- **Properties**: The `session.properties` array must have at least one property set (e.g. to identify the User ID).
|
|
28
|
+
- **RBAC Role Property**: If the authorization strategy is `RBAC`, the `session.properties` must also map the role property into the session payload.
|
|
29
|
+
- **Transports**: At least one session transport (such as `cookie` or `jwt`) must be configured and marked as `enabled`.
|
|
30
|
+
- **User Entity** [Error]: A designated `user` entity reference is required. This reference must point to a Data Entity that is appropriately annotated with the `User` semantic.
|
|
31
|
+
- **Action Access Rules** [Error]: Each configured operation (Action) must have at least one access rule defined that applies to it. This can be inherited from the API Model level, the Exposed Entity level, any parent Exposed Entity, or explicitly defined on the Action itself.
|
|
32
|
+
|
|
33
|
+
## 4. Exposures
|
|
34
|
+
- **Path Integrity** [Error]:
|
|
35
|
+
- If an exposure `hasCollection` is true, it MUST have a `collectionPath` defined.
|
|
36
|
+
- The `collectionPath` (if present) must contain exactly one segment starting with `/`.
|
|
37
|
+
- The `resourcePath` MUST be defined. If it has a collection, the resource path must be exactly the collection path plus a single parameter segment (e.g., `/products/{productId}`). If it does not have a collection, it must be exactly two segments (e.g. `/profile/{id}`).
|
|
38
|
+
- **Path Collisions** [Error]: For root exposures (`isRoot: true`), the `collectionPath` (if present) and `resourcePath` must be unique across the API model to prevent routing conflicts.
|
|
39
|
+
- **Valid Entity Reference** [Error]: Each exposed entity must reference a valid entity `key` that exists within the attached `DataDomain`.
|
|
40
|
+
- **Exposures Exist** [Warning]: If no entities are exposed, the API model will not generate any endpoints.
|
|
41
|
+
|
|
42
|
+
## 5. API Actions
|
|
43
|
+
- **Minimum Actions** [Error]: Each exposed entity must have at least one action added to it in the `actions` array.
|
|
44
|
+
- **Rate Limiting** [Warning]: It is recommended to set up rate limiting for exposed entities and individual actions to protect the API.
|
|
45
|
+
- **List Action** [Error/Warning]:
|
|
46
|
+
- **Pagination** [Error]: The `List` action strictly requires a pagination definition (`pagination.kind` must be defined as `offset` or `cursor`).
|
|
47
|
+
- **Filtering/Sorting** [Warning]: It is recommended to define `filterableFields` and `sortableFields`, but it is up to the user.
|
|
48
|
+
- **Search Action** [Error]: The `Search` action requires the user to define at least one indexable field in the `fields` array to perform the search on.
|
|
49
|
+
- **Delete Action** [Error/Info]:
|
|
50
|
+
- **Strategy** [Error]: The `Delete` action must define a deletion strategy (`soft` or `hard`).
|
|
51
|
+
- **Hard Delete Warning** [Info]: When the `hard` delete strategy is selected, print an info message that there will be no way to restore data.
|
|
52
|
+
- **Update Action** [Error]: The `Update` action must have at least one update method chosen in the `allowedMethods` array (e.g., `PUT` or `PATCH`).
|
|
53
|
+
|
|
54
|
+
## 6. Metadata
|
|
55
|
+
- **Contact Email** [Error]: If `contact.email` is provided, it MUST be in the format of a valid email address.
|
|
56
|
+
- **Contact URL** [Error]: If `contact.url` is provided, it MUST be in the format of a valid URL.
|
|
57
|
+
- **License URL** [Error]: If `license.url` is provided, it MUST be in the format of a valid URL.
|
|
58
|
+
- **Terms of Service / Contact / License info** [Info]: It's good practice to provide contact information, a license, and terms of service for a published API.
|
|
@@ -86,9 +86,7 @@ export class AssociationValidation {
|
|
|
86
86
|
return results
|
|
87
87
|
}
|
|
88
88
|
for (const target of association.targets) {
|
|
89
|
-
const entity = target.domain
|
|
90
|
-
? this.domain.findForeignEntity(target.key, target.domain)
|
|
91
|
-
: this.domain.findEntity(target.key)
|
|
89
|
+
const entity = this.domain.findEntity(target.key, target.domain)
|
|
92
90
|
if (!entity) {
|
|
93
91
|
const message = `The "${label}" association has an invalid target "${target.key}".`
|
|
94
92
|
const help = `The target must be defined in the domain.`
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { Action, AccessRule, RateLimitingConfiguration } from '../../../../src/modeling/index.js'
|
|
3
3
|
import { type ActionSchema } from '../../../../src/modeling/actions/Action.js'
|
|
4
|
+
import { type ExposedEntity } from '../../../../src/modeling/ExposedEntity.js'
|
|
4
5
|
|
|
5
6
|
test.group('Action', () => {
|
|
6
7
|
test('initializes with default values', ({ assert }) => {
|
|
7
|
-
const action = new Action()
|
|
8
|
+
const action = new Action({} as unknown as ExposedEntity)
|
|
8
9
|
assert.equal(action.kind, '')
|
|
9
10
|
assert.deepEqual(action.accessRule, [])
|
|
10
11
|
assert.isUndefined(action.rateLimiting)
|
|
@@ -16,7 +17,7 @@ test.group('Action', () => {
|
|
|
16
17
|
accessRule: [{ type: 'allowPublic' }],
|
|
17
18
|
rateLimiting: { rules: [] },
|
|
18
19
|
}
|
|
19
|
-
const action = new Action(schema)
|
|
20
|
+
const action = new Action({} as unknown as ExposedEntity, schema)
|
|
20
21
|
assert.equal(action.kind, 'read')
|
|
21
22
|
assert.lengthOf(action.accessRule, 1)
|
|
22
23
|
assert.instanceOf(action.accessRule[0], AccessRule)
|
|
@@ -25,7 +26,7 @@ test.group('Action', () => {
|
|
|
25
26
|
}).tags(['@modeling', '@action'])
|
|
26
27
|
|
|
27
28
|
test('serializes to JSON', ({ assert }) => {
|
|
28
|
-
const action = new Action({
|
|
29
|
+
const action = new Action({} as unknown as ExposedEntity, {
|
|
29
30
|
kind: 'write',
|
|
30
31
|
accessRule: [{ type: 'allowPublic' }],
|
|
31
32
|
rateLimiting: { rules: [] },
|
|
@@ -38,7 +39,7 @@ test.group('Action', () => {
|
|
|
38
39
|
}).tags(['@modeling', '@action'])
|
|
39
40
|
|
|
40
41
|
test('notifies change when kind changes', async ({ assert }) => {
|
|
41
|
-
const action = new Action({ kind: 'read' })
|
|
42
|
+
const action = new Action({} as unknown as ExposedEntity, { kind: 'read' })
|
|
42
43
|
let notified = false
|
|
43
44
|
action.addEventListener('change', () => {
|
|
44
45
|
notified = true
|
|
@@ -52,7 +53,7 @@ test.group('Action', () => {
|
|
|
52
53
|
}).tags(['@modeling', '@action', '@observed'])
|
|
53
54
|
|
|
54
55
|
test('notifies change when accessRule array is replaced', async ({ assert }) => {
|
|
55
|
-
const action = new Action()
|
|
56
|
+
const action = new Action({} as unknown as ExposedEntity)
|
|
56
57
|
let notified = false
|
|
57
58
|
action.addEventListener('change', () => {
|
|
58
59
|
notified = true
|
|
@@ -64,7 +65,7 @@ test.group('Action', () => {
|
|
|
64
65
|
}).tags(['@modeling', '@action', '@observed'])
|
|
65
66
|
|
|
66
67
|
test('notifies change when rateLimiting is replaced', async ({ assert }) => {
|
|
67
|
-
const action = new Action()
|
|
68
|
+
const action = new Action({} as unknown as ExposedEntity)
|
|
68
69
|
let notified = false
|
|
69
70
|
action.addEventListener('change', () => {
|
|
70
71
|
notified = true
|
|
@@ -77,7 +78,7 @@ test.group('Action', () => {
|
|
|
77
78
|
|
|
78
79
|
test('constructor copies accessRule array (immutability)', ({ assert }) => {
|
|
79
80
|
const rules = [{ type: 'allowPublic' }]
|
|
80
|
-
const action = new Action({ kind: 'read', accessRule: rules })
|
|
81
|
+
const action = new Action({} as unknown as ExposedEntity, { kind: 'read', accessRule: rules })
|
|
81
82
|
|
|
82
83
|
// Modify original array
|
|
83
84
|
rules.push({ type: 'other' })
|
|
@@ -88,7 +89,7 @@ test.group('Action', () => {
|
|
|
88
89
|
}).tags(['@modeling', '@action', '@immutability'])
|
|
89
90
|
|
|
90
91
|
test('toJSON returns safe copy', ({ assert }) => {
|
|
91
|
-
const action = new Action({
|
|
92
|
+
const action = new Action({} as unknown as ExposedEntity, {
|
|
92
93
|
kind: 'read',
|
|
93
94
|
accessRule: [{ type: 'allowPublic' }],
|
|
94
95
|
rateLimiting: { rules: [] },
|
|
@@ -106,4 +107,35 @@ test.group('Action', () => {
|
|
|
106
107
|
assert.lengthOf(action.accessRule, 1)
|
|
107
108
|
assert.equal(action.accessRule[0].type, 'allowPublic')
|
|
108
109
|
}).tags(['@modeling', '@action', '@immutability'])
|
|
110
|
+
|
|
111
|
+
test('getAllRules() aggregates rules from action and parent', ({ assert }) => {
|
|
112
|
+
const parent = {
|
|
113
|
+
getAllRules: () => [new AccessRule({ type: 'allowPublic' })],
|
|
114
|
+
} as unknown as ExposedEntity
|
|
115
|
+
|
|
116
|
+
const action = new Action(parent, {
|
|
117
|
+
accessRule: [{ type: 'allowAuthenticated' }],
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const rules = action.getAllRules()
|
|
121
|
+
assert.lengthOf(rules, 2)
|
|
122
|
+
assert.equal(rules[0].type, 'allowPublic')
|
|
123
|
+
assert.equal(rules[1].type, 'allowAuthenticated')
|
|
124
|
+
}).tags(['@modeling', '@action', '@rules'])
|
|
125
|
+
|
|
126
|
+
test('getAllRateLimiters() aggregates rate limiters from action and parent', ({ assert }) => {
|
|
127
|
+
const parentLimiter = new RateLimitingConfiguration({ rules: [{ rate: 10, interval: 'second' }] })
|
|
128
|
+
const parent = {
|
|
129
|
+
getAllRateLimiters: () => [parentLimiter],
|
|
130
|
+
} as unknown as ExposedEntity
|
|
131
|
+
|
|
132
|
+
const action = new Action(parent, {
|
|
133
|
+
rateLimiting: { rules: [{ rate: 20, interval: 'minute' }] },
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const limiters = action.getAllRateLimiters()
|
|
137
|
+
assert.lengthOf(limiters, 2)
|
|
138
|
+
assert.equal(limiters[0].rules[0].rate, 10)
|
|
139
|
+
assert.equal(limiters[1].rules[0].rate, 20)
|
|
140
|
+
}).tags(['@modeling', '@action', '@rate-limiting'])
|
|
109
141
|
})
|
|
@@ -4,13 +4,13 @@ import { AccessRule } from '../../../../src/modeling/rules/index.js'
|
|
|
4
4
|
|
|
5
5
|
test.group('CreateAction', () => {
|
|
6
6
|
test('initializes with default values', ({ assert }) => {
|
|
7
|
-
const action = new CreateAction()
|
|
7
|
+
const action = new CreateAction({} as any)
|
|
8
8
|
assert.equal(action.kind, 'create')
|
|
9
9
|
assert.isEmpty(action.accessRule) // Inherited from Action
|
|
10
10
|
}).tags(['@modeling', '@action', '@create-action'])
|
|
11
11
|
|
|
12
12
|
test('initializes with inherited values', ({ assert }) => {
|
|
13
|
-
const action = new CreateAction({
|
|
13
|
+
const action = new CreateAction({} as any, {
|
|
14
14
|
accessRule: [{ type: 'allowPublic' }],
|
|
15
15
|
})
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ test.group('CreateAction', () => {
|
|
|
22
22
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
23
23
|
const rules = [{ type: 'allowPublic' }]
|
|
24
24
|
|
|
25
|
-
const action = new CreateAction({
|
|
25
|
+
const action = new CreateAction({} as any, {
|
|
26
26
|
accessRule: rules,
|
|
27
27
|
})
|
|
28
28
|
|
|
@@ -35,7 +35,7 @@ test.group('CreateAction', () => {
|
|
|
35
35
|
}).tags(['@modeling', '@action', '@create-action', '@immutability'])
|
|
36
36
|
|
|
37
37
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
38
|
-
const action = new CreateAction({
|
|
38
|
+
const action = new CreateAction({} as any, {
|
|
39
39
|
accessRule: [{ type: 'allowPublic' }],
|
|
40
40
|
})
|
|
41
41
|
|
|
@@ -51,7 +51,7 @@ test.group('CreateAction', () => {
|
|
|
51
51
|
}).tags(['@modeling', '@action', '@create-action', '@serialization'])
|
|
52
52
|
|
|
53
53
|
test('notifies change when inherited property changes', async ({ assert }) => {
|
|
54
|
-
const action = new CreateAction()
|
|
54
|
+
const action = new CreateAction({} as any)
|
|
55
55
|
let notified = false
|
|
56
56
|
action.addEventListener('change', () => {
|
|
57
57
|
notified = true
|
|
@@ -3,7 +3,7 @@ import { DeleteAction } from '../../../../src/modeling/actions/DeleteAction.js'
|
|
|
3
3
|
|
|
4
4
|
test.group('DeleteAction', () => {
|
|
5
5
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const action = new DeleteAction()
|
|
6
|
+
const action = new DeleteAction({} as any)
|
|
7
7
|
assert.equal(action.kind, 'delete')
|
|
8
8
|
assert.equal(action.strategy, 'soft')
|
|
9
9
|
assert.equal(action.retentionPeriod, 30)
|
|
@@ -11,7 +11,7 @@ test.group('DeleteAction', () => {
|
|
|
11
11
|
}).tags(['@modeling', '@action', '@delete-action'])
|
|
12
12
|
|
|
13
13
|
test('initializes with provided values', ({ assert }) => {
|
|
14
|
-
const action = new DeleteAction({
|
|
14
|
+
const action = new DeleteAction({} as any, {
|
|
15
15
|
strategy: 'hard',
|
|
16
16
|
retentionPeriod: 0,
|
|
17
17
|
accessRule: [{ type: 'allowPublic' }],
|
|
@@ -27,7 +27,7 @@ test.group('DeleteAction', () => {
|
|
|
27
27
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
28
28
|
const rules = [{ type: 'allowPublic' }]
|
|
29
29
|
|
|
30
|
-
const action = new DeleteAction({
|
|
30
|
+
const action = new DeleteAction({} as any, {
|
|
31
31
|
accessRule: rules,
|
|
32
32
|
})
|
|
33
33
|
|
|
@@ -40,7 +40,7 @@ test.group('DeleteAction', () => {
|
|
|
40
40
|
}).tags(['@modeling', '@action', '@delete-action', '@immutability'])
|
|
41
41
|
|
|
42
42
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
43
|
-
const action = new DeleteAction({
|
|
43
|
+
const action = new DeleteAction({} as any, {
|
|
44
44
|
strategy: 'hard',
|
|
45
45
|
retentionPeriod: 0,
|
|
46
46
|
})
|
|
@@ -53,7 +53,7 @@ test.group('DeleteAction', () => {
|
|
|
53
53
|
}).tags(['@modeling', '@action', '@delete-action', '@serialization'])
|
|
54
54
|
|
|
55
55
|
test('notifies change when strategy changes', async ({ assert }) => {
|
|
56
|
-
const action = new DeleteAction()
|
|
56
|
+
const action = new DeleteAction({} as any)
|
|
57
57
|
let notified = false
|
|
58
58
|
action.addEventListener('change', () => {
|
|
59
59
|
notified = true
|
|
@@ -65,7 +65,7 @@ test.group('DeleteAction', () => {
|
|
|
65
65
|
}).tags(['@modeling', '@action', '@delete-action', '@observed'])
|
|
66
66
|
|
|
67
67
|
test('notifies change when retentionPeriod changes', async ({ assert }) => {
|
|
68
|
-
const action = new DeleteAction()
|
|
68
|
+
const action = new DeleteAction({} as any)
|
|
69
69
|
let notified = false
|
|
70
70
|
action.addEventListener('change', () => {
|
|
71
71
|
notified = true
|
|
@@ -4,7 +4,7 @@ import { PaginationStrategy } from '../../../../src/modeling/types.js'
|
|
|
4
4
|
|
|
5
5
|
test.group('ListAction', () => {
|
|
6
6
|
test('initializes with default values', ({ assert }) => {
|
|
7
|
-
const action = new ListAction()
|
|
7
|
+
const action = new ListAction({} as any)
|
|
8
8
|
assert.equal(action.kind, 'list')
|
|
9
9
|
assert.deepEqual(action.pagination, { kind: 'offset' })
|
|
10
10
|
assert.deepEqual(action.filterableFields, [])
|
|
@@ -17,7 +17,7 @@ test.group('ListAction', () => {
|
|
|
17
17
|
const filterableFields = ['name', 'status']
|
|
18
18
|
const sortableFields = ['createdAt']
|
|
19
19
|
|
|
20
|
-
const action = new ListAction({
|
|
20
|
+
const action = new ListAction({} as any, {
|
|
21
21
|
pagination,
|
|
22
22
|
filterableFields,
|
|
23
23
|
sortableFields,
|
|
@@ -33,7 +33,7 @@ test.group('ListAction', () => {
|
|
|
33
33
|
const filterableFields = ['name']
|
|
34
34
|
const sortableFields = ['name']
|
|
35
35
|
|
|
36
|
-
const action = new ListAction({
|
|
36
|
+
const action = new ListAction({} as any, {
|
|
37
37
|
pagination,
|
|
38
38
|
filterableFields,
|
|
39
39
|
sortableFields,
|
|
@@ -50,7 +50,7 @@ test.group('ListAction', () => {
|
|
|
50
50
|
}).tags(['@modeling', '@action', '@list-action', '@immutability'])
|
|
51
51
|
|
|
52
52
|
test('toJSON returns safe copy', ({ assert }) => {
|
|
53
|
-
const action = new ListAction({
|
|
53
|
+
const action = new ListAction({} as any, {
|
|
54
54
|
pagination: { kind: 'offset' },
|
|
55
55
|
filterableFields: ['name'],
|
|
56
56
|
sortableFields: ['name'],
|
|
@@ -69,7 +69,7 @@ test.group('ListAction', () => {
|
|
|
69
69
|
}).tags(['@modeling', '@action', '@list-action', '@immutability'])
|
|
70
70
|
|
|
71
71
|
test('notifies change when pagination changes', async ({ assert }) => {
|
|
72
|
-
const action = new ListAction()
|
|
72
|
+
const action = new ListAction({} as any)
|
|
73
73
|
let notified = false
|
|
74
74
|
action.addEventListener('change', () => {
|
|
75
75
|
notified = true
|
|
@@ -81,7 +81,7 @@ test.group('ListAction', () => {
|
|
|
81
81
|
}).tags(['@modeling', '@action', '@list-action', '@observed'])
|
|
82
82
|
|
|
83
83
|
test('notifies change when filterableFields changes', async ({ assert }) => {
|
|
84
|
-
const action = new ListAction()
|
|
84
|
+
const action = new ListAction({} as any)
|
|
85
85
|
let notified = false
|
|
86
86
|
action.addEventListener('change', () => {
|
|
87
87
|
notified = true
|
|
@@ -93,7 +93,7 @@ test.group('ListAction', () => {
|
|
|
93
93
|
}).tags(['@modeling', '@action', '@list-action', '@observed'])
|
|
94
94
|
|
|
95
95
|
test('notifies change when sortableFields changes', async ({ assert }) => {
|
|
96
|
-
const action = new ListAction()
|
|
96
|
+
const action = new ListAction({} as any)
|
|
97
97
|
let notified = false
|
|
98
98
|
action.addEventListener('change', () => {
|
|
99
99
|
notified = true
|
|
@@ -4,13 +4,13 @@ import { AccessRule } from '../../../../src/modeling/rules/index.js'
|
|
|
4
4
|
|
|
5
5
|
test.group('ReadAction', () => {
|
|
6
6
|
test('initializes with default values', ({ assert }) => {
|
|
7
|
-
const action = new ReadAction()
|
|
7
|
+
const action = new ReadAction({} as any)
|
|
8
8
|
assert.equal(action.kind, 'read')
|
|
9
9
|
assert.isEmpty(action.accessRule) // Inherited from Action
|
|
10
10
|
}).tags(['@modeling', '@action', '@read-action'])
|
|
11
11
|
|
|
12
12
|
test('initializes with inherited values', ({ assert }) => {
|
|
13
|
-
const action = new ReadAction({
|
|
13
|
+
const action = new ReadAction({} as any, {
|
|
14
14
|
accessRule: [{ type: 'allowPublic' }],
|
|
15
15
|
})
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ test.group('ReadAction', () => {
|
|
|
22
22
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
23
23
|
const rules = [{ type: 'allowPublic' }]
|
|
24
24
|
|
|
25
|
-
const action = new ReadAction({
|
|
25
|
+
const action = new ReadAction({} as any, {
|
|
26
26
|
accessRule: rules,
|
|
27
27
|
})
|
|
28
28
|
|
|
@@ -35,7 +35,7 @@ test.group('ReadAction', () => {
|
|
|
35
35
|
}).tags(['@modeling', '@action', '@read-action', '@immutability'])
|
|
36
36
|
|
|
37
37
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
38
|
-
const action = new ReadAction({
|
|
38
|
+
const action = new ReadAction({} as any, {
|
|
39
39
|
accessRule: [{ type: 'allowPublic' }],
|
|
40
40
|
})
|
|
41
41
|
|
|
@@ -51,7 +51,7 @@ test.group('ReadAction', () => {
|
|
|
51
51
|
}).tags(['@modeling', '@action', '@read-action', '@serialization'])
|
|
52
52
|
|
|
53
53
|
test('notifies change when inherited property changes', async ({ assert }) => {
|
|
54
|
-
const action = new ReadAction()
|
|
54
|
+
const action = new ReadAction({} as any)
|
|
55
55
|
let notified = false
|
|
56
56
|
action.addEventListener('change', () => {
|
|
57
57
|
notified = true
|
|
@@ -64,7 +64,7 @@ test.group('ReadAction', () => {
|
|
|
64
64
|
}).tags(['@modeling', '@action', '@read-action', '@observed'])
|
|
65
65
|
|
|
66
66
|
test('notifies change when accessRule value change', async ({ assert }) => {
|
|
67
|
-
const action = new ReadAction()
|
|
67
|
+
const action = new ReadAction({} as any)
|
|
68
68
|
let notified = false
|
|
69
69
|
action.addEventListener('change', () => {
|
|
70
70
|
notified = true
|
|
@@ -3,7 +3,7 @@ import { SearchAction } from '../../../../src/modeling/actions/SearchAction.js'
|
|
|
3
3
|
|
|
4
4
|
test.group('SearchAction', () => {
|
|
5
5
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const action = new SearchAction()
|
|
6
|
+
const action = new SearchAction({} as any)
|
|
7
7
|
assert.equal(action.kind, 'search')
|
|
8
8
|
assert.deepEqual(action.fields, [])
|
|
9
9
|
assert.isEmpty(action.accessRule)
|
|
@@ -11,7 +11,7 @@ test.group('SearchAction', () => {
|
|
|
11
11
|
|
|
12
12
|
test('initializes with provided values', ({ assert }) => {
|
|
13
13
|
const fields = ['name', 'description']
|
|
14
|
-
const action = new SearchAction({
|
|
14
|
+
const action = new SearchAction({} as any, {
|
|
15
15
|
fields,
|
|
16
16
|
})
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ test.group('SearchAction', () => {
|
|
|
22
22
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
23
23
|
const fields = ['name']
|
|
24
24
|
|
|
25
|
-
const action = new SearchAction({
|
|
25
|
+
const action = new SearchAction({} as any, {
|
|
26
26
|
fields,
|
|
27
27
|
})
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ test.group('SearchAction', () => {
|
|
|
33
33
|
}).tags(['@modeling', '@action', '@search-action', '@immutability'])
|
|
34
34
|
|
|
35
35
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
36
|
-
const action = new SearchAction({
|
|
36
|
+
const action = new SearchAction({} as any, {
|
|
37
37
|
fields: ['name'],
|
|
38
38
|
})
|
|
39
39
|
|
|
@@ -48,7 +48,7 @@ test.group('SearchAction', () => {
|
|
|
48
48
|
}).tags(['@modeling', '@action', '@search-action', '@serialization', '@immutability'])
|
|
49
49
|
|
|
50
50
|
test('notifies change when fields changes', async ({ assert }) => {
|
|
51
|
-
const action = new SearchAction()
|
|
51
|
+
const action = new SearchAction({} as any)
|
|
52
52
|
let notified = false
|
|
53
53
|
action.addEventListener('change', () => {
|
|
54
54
|
notified = true
|
|
@@ -60,7 +60,7 @@ test.group('SearchAction', () => {
|
|
|
60
60
|
}).tags(['@modeling', '@action', '@search-action', '@observed'])
|
|
61
61
|
|
|
62
62
|
test('notifies change when fields value change', async ({ assert }) => {
|
|
63
|
-
const action = new SearchAction()
|
|
63
|
+
const action = new SearchAction({} as any)
|
|
64
64
|
let notified = false
|
|
65
65
|
action.addEventListener('change', () => {
|
|
66
66
|
notified = true
|
|
@@ -3,7 +3,7 @@ import { UpdateAction } from '../../../../src/modeling/actions/UpdateAction.js'
|
|
|
3
3
|
|
|
4
4
|
test.group('UpdateAction', () => {
|
|
5
5
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const action = new UpdateAction()
|
|
6
|
+
const action = new UpdateAction({} as any)
|
|
7
7
|
assert.equal(action.kind, 'update')
|
|
8
8
|
assert.deepEqual(action.allowedMethods, ['PATCH'])
|
|
9
9
|
assert.isEmpty(action.accessRule)
|
|
@@ -11,7 +11,7 @@ test.group('UpdateAction', () => {
|
|
|
11
11
|
|
|
12
12
|
test('initializes with provided values', ({ assert }) => {
|
|
13
13
|
const methods: ('PUT' | 'PATCH')[] = ['PUT', 'PATCH']
|
|
14
|
-
const action = new UpdateAction({
|
|
14
|
+
const action = new UpdateAction({} as any, {
|
|
15
15
|
allowedMethods: methods,
|
|
16
16
|
})
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ test.group('UpdateAction', () => {
|
|
|
22
22
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
23
23
|
const methods: ('PUT' | 'PATCH')[] = ['PUT']
|
|
24
24
|
|
|
25
|
-
const action = new UpdateAction({
|
|
25
|
+
const action = new UpdateAction({} as any, {
|
|
26
26
|
allowedMethods: methods,
|
|
27
27
|
})
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ test.group('UpdateAction', () => {
|
|
|
33
33
|
}).tags(['@modeling', '@action', '@update-action', '@immutability'])
|
|
34
34
|
|
|
35
35
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
36
|
-
const action = new UpdateAction({
|
|
36
|
+
const action = new UpdateAction({} as any, {
|
|
37
37
|
allowedMethods: ['PUT'],
|
|
38
38
|
})
|
|
39
39
|
|
|
@@ -48,7 +48,7 @@ test.group('UpdateAction', () => {
|
|
|
48
48
|
}).tags(['@modeling', '@action', '@update-action', '@serialization', '@immutability'])
|
|
49
49
|
|
|
50
50
|
test('notifies change when allowedMethods changes', async ({ assert }) => {
|
|
51
|
-
const action = new UpdateAction()
|
|
51
|
+
const action = new UpdateAction({} as any)
|
|
52
52
|
let notified = false
|
|
53
53
|
action.addEventListener('change', () => {
|
|
54
54
|
notified = true
|
|
@@ -60,7 +60,7 @@ test.group('UpdateAction', () => {
|
|
|
60
60
|
}).tags(['@modeling', '@action', '@update-action', '@observed'])
|
|
61
61
|
|
|
62
62
|
test('notifies change when allowedMethods value change', async ({ assert }) => {
|
|
63
|
-
const action = new UpdateAction()
|
|
63
|
+
const action = new UpdateAction({} as any)
|
|
64
64
|
let notified = false
|
|
65
65
|
action.addEventListener('change', () => {
|
|
66
66
|
notified = true
|