@api-client/core 0.19.22 → 0.19.24
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/build/src/browser.d.ts +3 -0
- package/build/src/browser.d.ts.map +1 -1
- package/build/src/browser.js +2 -0
- package/build/src/browser.js.map +1 -1
- package/build/src/index.d.ts +3 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +2 -0
- package/build/src/index.js.map +1 -1
- package/build/src/modeling/ApiModel.d.ts.map +1 -1
- package/build/src/modeling/ApiModel.js +37 -13
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/ExposedEntity.d.ts.map +1 -1
- package/build/src/modeling/ExposedEntity.js +54 -15
- package/build/src/modeling/ExposedEntity.js.map +1 -1
- package/build/src/modeling/actions/Action.js +2 -2
- package/build/src/modeling/actions/Action.js.map +1 -1
- package/build/src/modeling/rules/AccessRule.d.ts +5 -1
- package/build/src/modeling/rules/AccessRule.d.ts.map +1 -1
- package/build/src/modeling/rules/AccessRule.js +4 -1
- package/build/src/modeling/rules/AccessRule.js.map +1 -1
- package/build/src/modeling/rules/AllowAuthenticated.d.ts +4 -1
- package/build/src/modeling/rules/AllowAuthenticated.d.ts.map +1 -1
- package/build/src/modeling/rules/AllowAuthenticated.js +2 -2
- package/build/src/modeling/rules/AllowAuthenticated.js.map +1 -1
- package/build/src/modeling/rules/AllowPublic.d.ts +4 -1
- package/build/src/modeling/rules/AllowPublic.d.ts.map +1 -1
- package/build/src/modeling/rules/AllowPublic.js +2 -2
- package/build/src/modeling/rules/AllowPublic.js.map +1 -1
- package/build/src/modeling/rules/MatchEmailDomain.d.ts +4 -1
- package/build/src/modeling/rules/MatchEmailDomain.d.ts.map +1 -1
- package/build/src/modeling/rules/MatchEmailDomain.js +2 -2
- package/build/src/modeling/rules/MatchEmailDomain.js.map +1 -1
- package/build/src/modeling/rules/MatchResourceOwner.d.ts +4 -1
- package/build/src/modeling/rules/MatchResourceOwner.d.ts.map +1 -1
- package/build/src/modeling/rules/MatchResourceOwner.js +2 -2
- package/build/src/modeling/rules/MatchResourceOwner.js.map +1 -1
- package/build/src/modeling/rules/MatchUserProperty.d.ts +4 -1
- package/build/src/modeling/rules/MatchUserProperty.d.ts.map +1 -1
- package/build/src/modeling/rules/MatchUserProperty.js +2 -2
- package/build/src/modeling/rules/MatchUserProperty.js.map +1 -1
- package/build/src/modeling/rules/MatchUserRole.d.ts +4 -1
- package/build/src/modeling/rules/MatchUserRole.d.ts.map +1 -1
- package/build/src/modeling/rules/MatchUserRole.js +2 -2
- package/build/src/modeling/rules/MatchUserRole.js.map +1 -1
- package/build/src/modeling/rules/index.d.ts +4 -1
- package/build/src/modeling/rules/index.d.ts.map +1 -1
- package/build/src/modeling/rules/index.js +7 -7
- package/build/src/modeling/rules/index.js.map +1 -1
- package/build/src/models/store/CustomDomain.d.ts +50 -0
- package/build/src/models/store/CustomDomain.d.ts.map +1 -0
- package/build/src/models/store/CustomDomain.js +79 -0
- package/build/src/models/store/CustomDomain.js.map +1 -0
- package/build/src/models/store/Deployment.d.ts +81 -0
- package/build/src/models/store/Deployment.d.ts.map +1 -0
- package/build/src/models/store/Deployment.js +124 -0
- package/build/src/models/store/Deployment.js.map +1 -0
- package/build/src/models/store/DeploymentCustomDomain.d.ts +52 -0
- package/build/src/models/store/DeploymentCustomDomain.d.ts.map +1 -0
- package/build/src/models/store/DeploymentCustomDomain.js +84 -0
- package/build/src/models/store/DeploymentCustomDomain.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/modeling/ApiModel.ts +37 -13
- package/src/modeling/ExposedEntity.ts +62 -16
- package/src/modeling/actions/Action.ts +2 -2
- package/src/modeling/rules/AccessRule.ts +8 -1
- package/src/modeling/rules/AllowAuthenticated.ts +5 -2
- package/src/modeling/rules/AllowPublic.ts +5 -2
- package/src/modeling/rules/MatchEmailDomain.ts +5 -2
- package/src/modeling/rules/MatchResourceOwner.ts +5 -2
- package/src/modeling/rules/MatchUserProperty.ts +5 -2
- package/src/modeling/rules/MatchUserRole.ts +5 -2
- package/src/models/store/CustomDomain.ts +119 -0
- package/src/models/store/Deployment.ts +173 -0
- package/src/models/store/DeploymentCustomDomain.ts +120 -0
- package/tests/unit/modeling/actions/Action.spec.ts +13 -10
- package/tests/unit/modeling/actions/CreateAction.spec.ts +7 -6
- package/tests/unit/modeling/actions/DeleteAction.spec.ts +7 -6
- package/tests/unit/modeling/actions/ListAction.spec.ts +5 -4
- package/tests/unit/modeling/actions/ReadAction.spec.ts +9 -8
- package/tests/unit/modeling/actions/SearchAction.spec.ts +5 -4
- package/tests/unit/modeling/actions/UpdateAction.spec.ts +7 -6
- package/tests/unit/modeling/actions/helpers.ts +7 -0
- package/tests/unit/modeling/api_model.spec.ts +3 -1
- package/tests/unit/modeling/api_model_expose_entity.spec.ts +5 -17
- package/tests/unit/modeling/exposed_entity.spec.ts +6 -2
- package/tests/unit/modeling/exposed_entity_actions.spec.ts +0 -4
- package/tests/unit/modeling/rules/AccessRule.spec.ts +6 -5
- package/tests/unit/modeling/rules/AllowAuthenticated.spec.ts +4 -3
- package/tests/unit/modeling/rules/AllowPublic.spec.ts +4 -3
- package/tests/unit/modeling/rules/MatchEmailDomain.spec.ts +6 -5
- package/tests/unit/modeling/rules/MatchResourceOwner.spec.ts +7 -6
- package/tests/unit/modeling/rules/MatchUserProperty.spec.ts +6 -5
- package/tests/unit/modeling/rules/MatchUserRole.spec.ts +6 -5
- package/tests/unit/modeling/rules/restoring_rules.spec.ts +19 -21
- package/tests/unit/models/store/CustomDomain.spec.ts +111 -0
- package/tests/unit/models/store/Deployment.spec.ts +134 -0
- package/tests/unit/models/store/DeploymentCustomDomain.spec.ts +122 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { UpdateAction } from '../../../../src/modeling/actions/UpdateAction.js'
|
|
3
|
+
import { mockExposedEntity } from './helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('UpdateAction', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const action = new UpdateAction(
|
|
7
|
+
const action = new UpdateAction(mockExposedEntity())
|
|
7
8
|
assert.equal(action.kind, 'update')
|
|
8
9
|
assert.deepEqual(action.allowedMethods, ['PATCH'])
|
|
9
10
|
assert.isEmpty(action.accessRule)
|
|
@@ -11,7 +12,7 @@ test.group('UpdateAction', () => {
|
|
|
11
12
|
|
|
12
13
|
test('initializes with provided values', ({ assert }) => {
|
|
13
14
|
const methods: ('PUT' | 'PATCH')[] = ['PUT', 'PATCH']
|
|
14
|
-
const action = new UpdateAction(
|
|
15
|
+
const action = new UpdateAction(mockExposedEntity(), {
|
|
15
16
|
allowedMethods: methods,
|
|
16
17
|
})
|
|
17
18
|
|
|
@@ -22,7 +23,7 @@ test.group('UpdateAction', () => {
|
|
|
22
23
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
23
24
|
const methods: ('PUT' | 'PATCH')[] = ['PUT']
|
|
24
25
|
|
|
25
|
-
const action = new UpdateAction(
|
|
26
|
+
const action = new UpdateAction(mockExposedEntity(), {
|
|
26
27
|
allowedMethods: methods,
|
|
27
28
|
})
|
|
28
29
|
|
|
@@ -33,7 +34,7 @@ test.group('UpdateAction', () => {
|
|
|
33
34
|
}).tags(['@modeling', '@action', '@update-action', '@immutability'])
|
|
34
35
|
|
|
35
36
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
36
|
-
const action = new UpdateAction(
|
|
37
|
+
const action = new UpdateAction(mockExposedEntity(), {
|
|
37
38
|
allowedMethods: ['PUT'],
|
|
38
39
|
})
|
|
39
40
|
|
|
@@ -48,7 +49,7 @@ test.group('UpdateAction', () => {
|
|
|
48
49
|
}).tags(['@modeling', '@action', '@update-action', '@serialization', '@immutability'])
|
|
49
50
|
|
|
50
51
|
test('notifies change when allowedMethods changes', async ({ assert }) => {
|
|
51
|
-
const action = new UpdateAction(
|
|
52
|
+
const action = new UpdateAction(mockExposedEntity())
|
|
52
53
|
let notified = false
|
|
53
54
|
action.addEventListener('change', () => {
|
|
54
55
|
notified = true
|
|
@@ -60,7 +61,7 @@ test.group('UpdateAction', () => {
|
|
|
60
61
|
}).tags(['@modeling', '@action', '@update-action', '@observed'])
|
|
61
62
|
|
|
62
63
|
test('notifies change when allowedMethods value change', async ({ assert }) => {
|
|
63
|
-
const action = new UpdateAction(
|
|
64
|
+
const action = new UpdateAction(mockExposedEntity())
|
|
64
65
|
let notified = false
|
|
65
66
|
action.addEventListener('change', () => {
|
|
66
67
|
notified = true
|
|
@@ -152,7 +152,7 @@ test.group('ApiModel.constructor()', () => {
|
|
|
152
152
|
assert.deepEqual(model.authentication, { strategy: 'UsernamePassword' })
|
|
153
153
|
assert.deepEqual(model.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
154
154
|
assert.deepEqual(model.session, { secret: 'secret', properties: ['email'] })
|
|
155
|
-
assert.deepEqual(model.accessRule, [new AllowPublicAccessRule()])
|
|
155
|
+
assert.deepEqual(model.accessRule, [new AllowPublicAccessRule(model)])
|
|
156
156
|
assert.deepEqual(model.rateLimiting, new RateLimitingConfiguration())
|
|
157
157
|
assert.equal(model.termsOfService, 'https://example.com/terms')
|
|
158
158
|
assert.deepEqual(model.contact, { name: 'John Doe', email: 'john.doe@example.com' })
|
|
@@ -223,6 +223,8 @@ test.group('ApiModel.constructor()', () => {
|
|
|
223
223
|
})
|
|
224
224
|
|
|
225
225
|
Array.from(model.exposes.values())[0]!.actions[0]!.kind = 'write'
|
|
226
|
+
// there are two microtasks on the notification path.
|
|
227
|
+
await Promise.resolve()
|
|
226
228
|
await Promise.resolve()
|
|
227
229
|
assert.isTrue(notified)
|
|
228
230
|
}).tags(['@modeling', '@api', '@observed'])
|
|
@@ -35,20 +35,6 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
35
35
|
assert.deepEqual(exposedEntity.actions, [])
|
|
36
36
|
}).tags(['@modeling', '@api'])
|
|
37
37
|
|
|
38
|
-
test('returns an existing entity if already exposed', ({ assert }) => {
|
|
39
|
-
const domain = new DataDomain()
|
|
40
|
-
domain.info.version = '1.0.0'
|
|
41
|
-
const dm = domain.addModel()
|
|
42
|
-
const e1 = dm.addEntity()
|
|
43
|
-
const model = new ApiModel()
|
|
44
|
-
model.attachDataDomain(domain)
|
|
45
|
-
const initialExposedEntity = model.exposeEntity({ key: e1.key })
|
|
46
|
-
const retrievedExposedEntity = model.exposeEntity({ key: e1.key })
|
|
47
|
-
|
|
48
|
-
assert.deepEqual(retrievedExposedEntity.toJSON(), initialExposedEntity.toJSON())
|
|
49
|
-
assert.equal(model.exposes.size, 1)
|
|
50
|
-
}).tags(['@modeling', '@api'])
|
|
51
|
-
|
|
52
38
|
test('notifies change when a new entity is exposed', async ({ assert }) => {
|
|
53
39
|
const domain = new DataDomain()
|
|
54
40
|
domain.info.version = '1.0.0'
|
|
@@ -65,7 +51,7 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
65
51
|
assert.isTrue(notified)
|
|
66
52
|
}).tags(['@modeling', '@api'])
|
|
67
53
|
|
|
68
|
-
test('
|
|
54
|
+
test('throws if entity already exposed', async ({ assert }) => {
|
|
69
55
|
const domain = new DataDomain()
|
|
70
56
|
domain.info.version = '1.0.0'
|
|
71
57
|
const dm = domain.addModel()
|
|
@@ -78,9 +64,11 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
78
64
|
model.addEventListener('change', () => {
|
|
79
65
|
notified = true
|
|
80
66
|
})
|
|
81
|
-
|
|
67
|
+
await assert.rejects(async () => {
|
|
68
|
+
model.exposeEntity({ key: e1.key }) // Second exposure
|
|
69
|
+
}, `Entity ${e1.key} is already exposed.`)
|
|
82
70
|
await Promise.resolve() // Allow microtask to run
|
|
83
|
-
assert.isFalse(notified)
|
|
71
|
+
assert.isFalse(notified, 'should not notify change if entity already exposed')
|
|
84
72
|
}).tags(['@modeling', '@api'])
|
|
85
73
|
|
|
86
74
|
test('exposes nested entities through associations', ({ assert }) => {
|
|
@@ -127,7 +127,9 @@ test.group('ExposedEntity', () => {
|
|
|
127
127
|
|
|
128
128
|
const ex = Array.from(model.exposes.values())[0]
|
|
129
129
|
ex.setCollectionPath('items')
|
|
130
|
-
|
|
130
|
+
// there are two microtasks on the notification path.
|
|
131
|
+
await Promise.resolve()
|
|
132
|
+
await Promise.resolve()
|
|
131
133
|
assert.isAtLeast(notified, 1)
|
|
132
134
|
}).tags(['@modeling', '@exposed-entity', '@observed'])
|
|
133
135
|
|
|
@@ -154,6 +156,8 @@ test.group('ExposedEntity', () => {
|
|
|
154
156
|
|
|
155
157
|
const ex = Array.from(model.exposes.values())[0]
|
|
156
158
|
ex.setResourcePath('/products/{productId}')
|
|
159
|
+
// there are two microtasks on the notification path.
|
|
160
|
+
await Promise.resolve()
|
|
157
161
|
await Promise.resolve()
|
|
158
162
|
assert.isAtLeast(notified, 1)
|
|
159
163
|
}).tags(['@modeling', '@exposed-entity', '@observed'])
|
|
@@ -304,7 +308,7 @@ test.group('ExposedEntity', () => {
|
|
|
304
308
|
|
|
305
309
|
test('getAllRules() aggregates rules from entity, parent, and API', ({ assert }) => {
|
|
306
310
|
const model = new ApiModel()
|
|
307
|
-
model.accessRule = [new AccessRule({ type: 'allowPublic' })]
|
|
311
|
+
model.accessRule = [new AccessRule(model, { type: 'allowPublic' })]
|
|
308
312
|
|
|
309
313
|
const rootEx = new ExposedEntity(model, {
|
|
310
314
|
key: 'root',
|
|
@@ -33,9 +33,6 @@ test.group('ExposedEntity::actions', (group) => {
|
|
|
33
33
|
test('restores a list acton', ({ assert }) => {
|
|
34
34
|
const action: ListActionSchema = {
|
|
35
35
|
kind: 'list',
|
|
36
|
-
pagination: { kind: '' },
|
|
37
|
-
sortableFields: [],
|
|
38
|
-
filterableFields: [],
|
|
39
36
|
}
|
|
40
37
|
const model = new ApiModel(
|
|
41
38
|
{
|
|
@@ -177,7 +174,6 @@ test.group('ExposedEntity::actions', (group) => {
|
|
|
177
174
|
test('restores a search acton', ({ assert }) => {
|
|
178
175
|
const action: SearchActionSchema = {
|
|
179
176
|
kind: 'search',
|
|
180
|
-
fields: [],
|
|
181
177
|
}
|
|
182
178
|
const model = new ApiModel(
|
|
183
179
|
{
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { AccessRule, type AccessRuleSchema } from '../../../../src/modeling/index.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('AccessRule', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const rule = new AccessRule()
|
|
7
|
+
const rule = new AccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, '')
|
|
8
9
|
}).tags(['@modeling', '@access-rule'])
|
|
9
10
|
|
|
10
11
|
test('initializes with provided values', ({ assert }) => {
|
|
11
12
|
const schema: AccessRuleSchema = { type: 'public' }
|
|
12
|
-
const rule = new AccessRule(schema)
|
|
13
|
+
const rule = new AccessRule(mockExposedEntity(), schema)
|
|
13
14
|
assert.equal(rule.type, 'public')
|
|
14
15
|
}).tags(['@modeling', '@access-rule'])
|
|
15
16
|
|
|
16
17
|
test('serializes to JSON', ({ assert }) => {
|
|
17
|
-
const rule = new AccessRule({ type: 'authenticated' })
|
|
18
|
+
const rule = new AccessRule(mockExposedEntity(), { type: 'authenticated' })
|
|
18
19
|
const json = rule.toJSON()
|
|
19
20
|
assert.deepEqual(json, { type: 'authenticated' })
|
|
20
21
|
}).tags(['@modeling', '@access-rule'])
|
|
21
22
|
|
|
22
23
|
test('notifies change', async ({ assert }) => {
|
|
23
|
-
const rule = new AccessRule({ type: 'public' })
|
|
24
|
+
const rule = new AccessRule(mockExposedEntity(), { type: 'public' })
|
|
24
25
|
let notified = false
|
|
25
26
|
rule.addEventListener('change', () => {
|
|
26
27
|
notified = true
|
|
@@ -31,7 +32,7 @@ test.group('AccessRule', () => {
|
|
|
31
32
|
}).tags(['@modeling', '@access-rule'])
|
|
32
33
|
|
|
33
34
|
test('toJSON returns safe copy (immutability)', ({ assert }) => {
|
|
34
|
-
const rule = new AccessRule({ type: 'public' })
|
|
35
|
+
const rule = new AccessRule(mockExposedEntity(), { type: 'public' })
|
|
35
36
|
const json = rule.toJSON()
|
|
36
37
|
|
|
37
38
|
// Modify JSON (simulate runtime mutation)
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { AllowAuthenticatedAccessRule } from '../../../../src/modeling/rules/AllowAuthenticated.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('AllowAuthenticatedAccessRule', () => {
|
|
5
6
|
test('initializes with correct type', ({ assert }) => {
|
|
6
|
-
const rule = new AllowAuthenticatedAccessRule()
|
|
7
|
+
const rule = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'allowAuthenticated')
|
|
8
9
|
}).tags(['@modeling', '@rule', '@allow-authenticated'])
|
|
9
10
|
|
|
10
11
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
11
|
-
const rule = new AllowAuthenticatedAccessRule()
|
|
12
|
+
const rule = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
12
13
|
const json = rule.toJSON()
|
|
13
14
|
|
|
14
15
|
assert.equal(json.type, 'allowAuthenticated')
|
|
15
16
|
}).tags(['@modeling', '@rule', '@allow-authenticated', '@serialization'])
|
|
16
17
|
|
|
17
18
|
test('notifies change', async ({ assert }) => {
|
|
18
|
-
const rule = new AllowAuthenticatedAccessRule()
|
|
19
|
+
const rule = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
19
20
|
let notified = false
|
|
20
21
|
rule.addEventListener('change', () => {
|
|
21
22
|
notified = true
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { AllowPublicAccessRule } from '../../../../src/modeling/rules/AllowPublic.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('AllowPublicAccessRule', () => {
|
|
5
6
|
test('initializes with correct type', ({ assert }) => {
|
|
6
|
-
const rule = new AllowPublicAccessRule()
|
|
7
|
+
const rule = new AllowPublicAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'allowPublic')
|
|
8
9
|
}).tags(['@modeling', '@rule', '@allow-public'])
|
|
9
10
|
|
|
10
11
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
11
|
-
const rule = new AllowPublicAccessRule()
|
|
12
|
+
const rule = new AllowPublicAccessRule(mockExposedEntity())
|
|
12
13
|
const json = rule.toJSON()
|
|
13
14
|
|
|
14
15
|
assert.equal(json.type, 'allowPublic')
|
|
15
16
|
}).tags(['@modeling', '@rule', '@allow-public', '@serialization'])
|
|
16
17
|
|
|
17
18
|
test('notifies change', async ({ assert }) => {
|
|
18
|
-
const rule = new AllowPublicAccessRule()
|
|
19
|
+
const rule = new AllowPublicAccessRule(mockExposedEntity())
|
|
19
20
|
let notified = false
|
|
20
21
|
rule.addEventListener('change', () => {
|
|
21
22
|
notified = true
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { MatchEmailDomainAccessRule } from '../../../../src/modeling/rules/MatchEmailDomain.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('MatchEmailDomainAccessRule', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const rule = new MatchEmailDomainAccessRule()
|
|
7
|
+
const rule = new MatchEmailDomainAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'matchEmailDomain')
|
|
8
9
|
assert.deepEqual(rule.domains, [])
|
|
9
10
|
}).tags(['@modeling', '@rule', '@match-email-domain'])
|
|
10
11
|
|
|
11
12
|
test('initializes with provided values', ({ assert }) => {
|
|
12
13
|
const domains = ['example.com', 'test.org']
|
|
13
|
-
const rule = new MatchEmailDomainAccessRule({ domains })
|
|
14
|
+
const rule = new MatchEmailDomainAccessRule(mockExposedEntity(), { domains })
|
|
14
15
|
|
|
15
16
|
assert.equal(rule.type, 'matchEmailDomain')
|
|
16
17
|
assert.deepEqual(rule.domains, domains)
|
|
@@ -18,7 +19,7 @@ test.group('MatchEmailDomainAccessRule', () => {
|
|
|
18
19
|
|
|
19
20
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
20
21
|
const domains = ['example.com']
|
|
21
|
-
const rule = new MatchEmailDomainAccessRule({ domains })
|
|
22
|
+
const rule = new MatchEmailDomainAccessRule(mockExposedEntity(), { domains })
|
|
22
23
|
|
|
23
24
|
// Modify original source
|
|
24
25
|
domains.push('hacker.com')
|
|
@@ -27,7 +28,7 @@ test.group('MatchEmailDomainAccessRule', () => {
|
|
|
27
28
|
}).tags(['@modeling', '@rule', '@match-email-domain', '@immutability'])
|
|
28
29
|
|
|
29
30
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
30
|
-
const rule = new MatchEmailDomainAccessRule({ domains: ['example.com'] })
|
|
31
|
+
const rule = new MatchEmailDomainAccessRule(mockExposedEntity(), { domains: ['example.com'] })
|
|
31
32
|
const json = rule.toJSON()
|
|
32
33
|
|
|
33
34
|
// Modify JSON
|
|
@@ -39,7 +40,7 @@ test.group('MatchEmailDomainAccessRule', () => {
|
|
|
39
40
|
}).tags(['@modeling', '@rule', '@match-email-domain', '@serialization', '@immutability'])
|
|
40
41
|
|
|
41
42
|
test('notifies change when domains changes', async ({ assert }) => {
|
|
42
|
-
const rule = new MatchEmailDomainAccessRule()
|
|
43
|
+
const rule = new MatchEmailDomainAccessRule(mockExposedEntity())
|
|
43
44
|
let notified = false
|
|
44
45
|
rule.addEventListener('change', () => {
|
|
45
46
|
notified = true
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { MatchResourceOwnerAccessRule } from '../../../../src/modeling/rules/MatchResourceOwner.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('MatchResourceOwnerAccessRule', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const rule = new MatchResourceOwnerAccessRule()
|
|
7
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'matchResourceOwner')
|
|
8
9
|
assert.isUndefined(rule.property)
|
|
9
10
|
assert.equal(rule.target, 'property')
|
|
10
11
|
}).tags(['@modeling', '@rule', '@match-resource-owner'])
|
|
11
12
|
|
|
12
13
|
test('initializes with provided values', ({ assert }) => {
|
|
13
|
-
const rule = new MatchResourceOwnerAccessRule({ property: 'creatorId' })
|
|
14
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity(), { property: 'creatorId' })
|
|
14
15
|
|
|
15
16
|
assert.equal(rule.type, 'matchResourceOwner')
|
|
16
17
|
assert.equal(rule.property, 'creatorId')
|
|
@@ -18,7 +19,7 @@ test.group('MatchResourceOwnerAccessRule', () => {
|
|
|
18
19
|
}).tags(['@modeling', '@rule', '@match-resource-owner'])
|
|
19
20
|
|
|
20
21
|
test('initializes with target user-entity', ({ assert }) => {
|
|
21
|
-
const rule = new MatchResourceOwnerAccessRule({ target: 'user-entity' })
|
|
22
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity(), { target: 'user-entity' })
|
|
22
23
|
|
|
23
24
|
assert.equal(rule.type, 'matchResourceOwner')
|
|
24
25
|
assert.isUndefined(rule.property)
|
|
@@ -26,7 +27,7 @@ test.group('MatchResourceOwnerAccessRule', () => {
|
|
|
26
27
|
}).tags(['@modeling', '@rule', '@match-resource-owner'])
|
|
27
28
|
|
|
28
29
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
29
|
-
const rule = new MatchResourceOwnerAccessRule({ property: 'creatorId' })
|
|
30
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity(), { property: 'creatorId' })
|
|
30
31
|
const json = rule.toJSON()
|
|
31
32
|
|
|
32
33
|
assert.equal(json.type, 'matchResourceOwner')
|
|
@@ -35,7 +36,7 @@ test.group('MatchResourceOwnerAccessRule', () => {
|
|
|
35
36
|
}).tags(['@modeling', '@rule', '@match-resource-owner', '@serialization'])
|
|
36
37
|
|
|
37
38
|
test('toJSON omits property when undefined', ({ assert }) => {
|
|
38
|
-
const rule = new MatchResourceOwnerAccessRule({ target: 'user-entity' })
|
|
39
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity(), { target: 'user-entity' })
|
|
39
40
|
const json = rule.toJSON()
|
|
40
41
|
|
|
41
42
|
assert.equal(json.type, 'matchResourceOwner')
|
|
@@ -44,7 +45,7 @@ test.group('MatchResourceOwnerAccessRule', () => {
|
|
|
44
45
|
}).tags(['@modeling', '@rule', '@match-resource-owner', '@serialization'])
|
|
45
46
|
|
|
46
47
|
test('notifies change when property changes', async ({ assert }) => {
|
|
47
|
-
const rule = new MatchResourceOwnerAccessRule()
|
|
48
|
+
const rule = new MatchResourceOwnerAccessRule(mockExposedEntity())
|
|
48
49
|
let notified = false
|
|
49
50
|
rule.addEventListener('change', () => {
|
|
50
51
|
notified = true
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { MatchUserPropertyAccessRule } from '../../../../src/modeling/rules/MatchUserProperty.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('MatchUserPropertyAccessRule', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const rule = new MatchUserPropertyAccessRule()
|
|
7
|
+
const rule = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'matchUserProperty')
|
|
8
9
|
assert.equal(rule.property, '')
|
|
9
10
|
assert.equal(rule.value, '')
|
|
10
11
|
}).tags(['@modeling', '@rule', '@match-user-property'])
|
|
11
12
|
|
|
12
13
|
test('initializes with provided values', ({ assert }) => {
|
|
13
|
-
const rule = new MatchUserPropertyAccessRule({
|
|
14
|
+
const rule = new MatchUserPropertyAccessRule(mockExposedEntity(), {
|
|
14
15
|
property: 'department',
|
|
15
16
|
value: 'engineering',
|
|
16
17
|
})
|
|
@@ -21,7 +22,7 @@ test.group('MatchUserPropertyAccessRule', () => {
|
|
|
21
22
|
}).tags(['@modeling', '@rule', '@match-user-property'])
|
|
22
23
|
|
|
23
24
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
24
|
-
const rule = new MatchUserPropertyAccessRule({
|
|
25
|
+
const rule = new MatchUserPropertyAccessRule(mockExposedEntity(), {
|
|
25
26
|
property: 'department',
|
|
26
27
|
value: 'engineering',
|
|
27
28
|
})
|
|
@@ -33,7 +34,7 @@ test.group('MatchUserPropertyAccessRule', () => {
|
|
|
33
34
|
}).tags(['@modeling', '@rule', '@match-user-property', '@serialization'])
|
|
34
35
|
|
|
35
36
|
test('notifies change when property changes', async ({ assert }) => {
|
|
36
|
-
const rule = new MatchUserPropertyAccessRule()
|
|
37
|
+
const rule = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
37
38
|
let notified = false
|
|
38
39
|
rule.addEventListener('change', () => {
|
|
39
40
|
notified = true
|
|
@@ -45,7 +46,7 @@ test.group('MatchUserPropertyAccessRule', () => {
|
|
|
45
46
|
}).tags(['@modeling', '@rule', '@match-user-property', '@observed'])
|
|
46
47
|
|
|
47
48
|
test('notifies change when value changes', async ({ assert }) => {
|
|
48
|
-
const rule = new MatchUserPropertyAccessRule()
|
|
49
|
+
const rule = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
49
50
|
let notified = false
|
|
50
51
|
rule.addEventListener('change', () => {
|
|
51
52
|
notified = true
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { MatchUserRoleAccessRule } from '../../../../src/modeling/rules/MatchUserRole.js'
|
|
3
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
3
4
|
|
|
4
5
|
test.group('MatchUserRoleAccessRule', () => {
|
|
5
6
|
test('initializes with default values', ({ assert }) => {
|
|
6
|
-
const rule = new MatchUserRoleAccessRule()
|
|
7
|
+
const rule = new MatchUserRoleAccessRule(mockExposedEntity())
|
|
7
8
|
assert.equal(rule.type, 'matchUserRole')
|
|
8
9
|
assert.deepEqual(rule.role, [])
|
|
9
10
|
}).tags(['@modeling', '@rule', '@match-user-role'])
|
|
10
11
|
|
|
11
12
|
test('initializes with provided values', ({ assert }) => {
|
|
12
13
|
const role = ['admin', 'manager']
|
|
13
|
-
const rule = new MatchUserRoleAccessRule({ role })
|
|
14
|
+
const rule = new MatchUserRoleAccessRule(mockExposedEntity(), { role })
|
|
14
15
|
|
|
15
16
|
assert.equal(rule.type, 'matchUserRole')
|
|
16
17
|
assert.deepEqual(rule.role, role)
|
|
@@ -18,7 +19,7 @@ test.group('MatchUserRoleAccessRule', () => {
|
|
|
18
19
|
|
|
19
20
|
test('constructor copies arrays (immutability)', ({ assert }) => {
|
|
20
21
|
const role = ['admin']
|
|
21
|
-
const rule = new MatchUserRoleAccessRule({ role })
|
|
22
|
+
const rule = new MatchUserRoleAccessRule(mockExposedEntity(), { role })
|
|
22
23
|
|
|
23
24
|
// Modify original source
|
|
24
25
|
role.push('guest')
|
|
@@ -27,7 +28,7 @@ test.group('MatchUserRoleAccessRule', () => {
|
|
|
27
28
|
}).tags(['@modeling', '@rule', '@match-user-role', '@immutability'])
|
|
28
29
|
|
|
29
30
|
test('toJSON returns valid schema', ({ assert }) => {
|
|
30
|
-
const rule = new MatchUserRoleAccessRule({ role: ['admin'] })
|
|
31
|
+
const rule = new MatchUserRoleAccessRule(mockExposedEntity(), { role: ['admin'] })
|
|
31
32
|
const json = rule.toJSON()
|
|
32
33
|
|
|
33
34
|
// Modify JSON
|
|
@@ -39,7 +40,7 @@ test.group('MatchUserRoleAccessRule', () => {
|
|
|
39
40
|
}).tags(['@modeling', '@rule', '@match-user-role', '@serialization', '@immutability'])
|
|
40
41
|
|
|
41
42
|
test('notifies change when role changes', async ({ assert }) => {
|
|
42
|
-
const rule = new MatchUserRoleAccessRule()
|
|
43
|
+
const rule = new MatchUserRoleAccessRule(mockExposedEntity())
|
|
43
44
|
let notified = false
|
|
44
45
|
rule.addEventListener('change', () => {
|
|
45
46
|
notified = true
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../../../src/modeling/index.js'
|
|
11
11
|
import { nanoid } from '../../../../src/nanoid.js'
|
|
12
12
|
import { ExposedEntityKind } from '../../../../src/models/kinds.js'
|
|
13
|
+
import { mockExposedEntity } from '../actions/helpers.js'
|
|
13
14
|
|
|
14
15
|
test.group('restoring actions', (group) => {
|
|
15
16
|
let domain: DataDomain
|
|
@@ -23,12 +24,12 @@ test.group('restoring actions', (group) => {
|
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
test('restores rules on the API model', ({ assert }) => {
|
|
26
|
-
const r1 = new AllowAuthenticatedAccessRule()
|
|
27
|
-
const r2 = new AllowPublicAccessRule()
|
|
28
|
-
const r3 = new MatchEmailDomainAccessRule()
|
|
29
|
-
const r4 = new MatchResourceOwnerAccessRule()
|
|
30
|
-
const r5 = new MatchUserPropertyAccessRule()
|
|
31
|
-
const r6 = new MatchUserRoleAccessRule()
|
|
27
|
+
const r1 = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
28
|
+
const r2 = new AllowPublicAccessRule(mockExposedEntity())
|
|
29
|
+
const r3 = new MatchEmailDomainAccessRule(mockExposedEntity())
|
|
30
|
+
const r4 = new MatchResourceOwnerAccessRule(mockExposedEntity())
|
|
31
|
+
const r5 = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
32
|
+
const r6 = new MatchUserRoleAccessRule(mockExposedEntity())
|
|
32
33
|
const model = new ApiModel(
|
|
33
34
|
{
|
|
34
35
|
accessRule: [r1.toJSON(), r2.toJSON(), r3.toJSON(), r4.toJSON(), r5.toJSON(), r6.toJSON()],
|
|
@@ -46,12 +47,12 @@ test.group('restoring actions', (group) => {
|
|
|
46
47
|
}).tags(['@modeling', '@rule', '@restoring'])
|
|
47
48
|
|
|
48
49
|
test('restores rules on the exposed entity', ({ assert }) => {
|
|
49
|
-
const r1 = new AllowAuthenticatedAccessRule()
|
|
50
|
-
const r2 = new AllowPublicAccessRule()
|
|
51
|
-
const r3 = new MatchEmailDomainAccessRule()
|
|
52
|
-
const r4 = new MatchResourceOwnerAccessRule()
|
|
53
|
-
const r5 = new MatchUserPropertyAccessRule()
|
|
54
|
-
const r6 = new MatchUserRoleAccessRule()
|
|
50
|
+
const r1 = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
51
|
+
const r2 = new AllowPublicAccessRule(mockExposedEntity())
|
|
52
|
+
const r3 = new MatchEmailDomainAccessRule(mockExposedEntity())
|
|
53
|
+
const r4 = new MatchResourceOwnerAccessRule(mockExposedEntity())
|
|
54
|
+
const r5 = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
55
|
+
const r6 = new MatchUserRoleAccessRule(mockExposedEntity())
|
|
55
56
|
const model = new ApiModel(
|
|
56
57
|
{
|
|
57
58
|
exposes: [
|
|
@@ -82,17 +83,14 @@ test.group('restoring actions', (group) => {
|
|
|
82
83
|
}).tags(['@modeling', '@rule', '@restoring'])
|
|
83
84
|
|
|
84
85
|
test('restores rules on the action', ({ assert }) => {
|
|
85
|
-
const r1 = new AllowAuthenticatedAccessRule()
|
|
86
|
-
const r2 = new AllowPublicAccessRule()
|
|
87
|
-
const r3 = new MatchEmailDomainAccessRule()
|
|
88
|
-
const r4 = new MatchResourceOwnerAccessRule()
|
|
89
|
-
const r5 = new MatchUserPropertyAccessRule()
|
|
90
|
-
const r6 = new MatchUserRoleAccessRule()
|
|
86
|
+
const r1 = new AllowAuthenticatedAccessRule(mockExposedEntity())
|
|
87
|
+
const r2 = new AllowPublicAccessRule(mockExposedEntity())
|
|
88
|
+
const r3 = new MatchEmailDomainAccessRule(mockExposedEntity())
|
|
89
|
+
const r4 = new MatchResourceOwnerAccessRule(mockExposedEntity())
|
|
90
|
+
const r5 = new MatchUserPropertyAccessRule(mockExposedEntity())
|
|
91
|
+
const r6 = new MatchUserRoleAccessRule(mockExposedEntity())
|
|
91
92
|
const action: ListActionSchema = {
|
|
92
93
|
kind: 'list',
|
|
93
|
-
pagination: { kind: '' },
|
|
94
|
-
sortableFields: [],
|
|
95
|
-
filterableFields: [],
|
|
96
94
|
accessRule: [r1.toJSON(), r2.toJSON(), r3.toJSON(), r4.toJSON(), r5.toJSON(), r6.toJSON()],
|
|
97
95
|
}
|
|
98
96
|
const model = new ApiModel(
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import { CustomDomainModel } from '../../../../src/models/store/CustomDomain.js'
|
|
3
|
+
import type { CustomDomainSchema } from '../../../../src/models/store/CustomDomain.js'
|
|
4
|
+
|
|
5
|
+
test.group('CustomDomain model', () => {
|
|
6
|
+
test('createSchema() returns default values', ({ assert }) => {
|
|
7
|
+
const result = CustomDomainModel.createSchema()
|
|
8
|
+
assert.isString(result.id)
|
|
9
|
+
assert.equal(result.orgId, '')
|
|
10
|
+
assert.equal(result.domain, '')
|
|
11
|
+
assert.equal(result.dnsTarget, '')
|
|
12
|
+
assert.isFalse(result.dnsVerified)
|
|
13
|
+
assert.isUndefined(result.lastVerifiedAt)
|
|
14
|
+
assert.isNumber(result.createdAt)
|
|
15
|
+
assert.isNumber(result.updatedAt)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('createSchema(init) assigns values', ({ assert }) => {
|
|
19
|
+
const init: Partial<CustomDomainSchema> = {
|
|
20
|
+
id: 'test-id',
|
|
21
|
+
orgId: 'org-1',
|
|
22
|
+
domain: 'api.example.com',
|
|
23
|
+
dnsTarget: 'target.example.com',
|
|
24
|
+
dnsVerified: true,
|
|
25
|
+
lastVerifiedAt: 11111,
|
|
26
|
+
createdAt: 12345,
|
|
27
|
+
updatedAt: 67890,
|
|
28
|
+
}
|
|
29
|
+
const result = CustomDomainModel.createSchema(init)
|
|
30
|
+
assert.equal(result.id, 'test-id')
|
|
31
|
+
assert.equal(result.orgId, 'org-1')
|
|
32
|
+
assert.equal(result.domain, 'api.example.com')
|
|
33
|
+
assert.equal(result.dnsTarget, 'target.example.com')
|
|
34
|
+
assert.isTrue(result.dnsVerified)
|
|
35
|
+
assert.equal(result.lastVerifiedAt, 11111)
|
|
36
|
+
assert.equal(result.createdAt, 12345)
|
|
37
|
+
assert.equal(result.updatedAt, 67890)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('constructor() initializes with default values', ({ assert }) => {
|
|
41
|
+
const model = new CustomDomainModel()
|
|
42
|
+
assert.isString(model.id)
|
|
43
|
+
assert.equal(model.orgId, '')
|
|
44
|
+
assert.equal(model.domain, '')
|
|
45
|
+
assert.equal(model.dnsTarget, '')
|
|
46
|
+
assert.isFalse(model.dnsVerified)
|
|
47
|
+
assert.isUndefined(model.lastVerifiedAt)
|
|
48
|
+
assert.isNumber(model.createdAt)
|
|
49
|
+
assert.isNumber(model.updatedAt)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('constructor(init) assigns values', ({ assert }) => {
|
|
53
|
+
const init: Partial<CustomDomainSchema> = {
|
|
54
|
+
orgId: 'org-1',
|
|
55
|
+
domain: 'api.example.com',
|
|
56
|
+
dnsVerified: true,
|
|
57
|
+
}
|
|
58
|
+
const model = new CustomDomainModel(init)
|
|
59
|
+
assert.equal(model.orgId, 'org-1')
|
|
60
|
+
assert.equal(model.domain, 'api.example.com')
|
|
61
|
+
assert.isTrue(model.dnsVerified)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('toJSON() returns the schema representation', ({ assert }) => {
|
|
65
|
+
const init: Partial<CustomDomainSchema> = {
|
|
66
|
+
id: 'test-id',
|
|
67
|
+
orgId: 'org-1',
|
|
68
|
+
domain: 'api.example.com',
|
|
69
|
+
dnsTarget: 'target.example.com',
|
|
70
|
+
dnsVerified: true,
|
|
71
|
+
lastVerifiedAt: 11111,
|
|
72
|
+
createdAt: 12345,
|
|
73
|
+
updatedAt: 67890,
|
|
74
|
+
}
|
|
75
|
+
const model = new CustomDomainModel(init)
|
|
76
|
+
const result = model.toJSON()
|
|
77
|
+
assert.deepEqual(result, {
|
|
78
|
+
id: 'test-id',
|
|
79
|
+
orgId: 'org-1',
|
|
80
|
+
domain: 'api.example.com',
|
|
81
|
+
dnsTarget: 'target.example.com',
|
|
82
|
+
dnsVerified: true,
|
|
83
|
+
lastVerifiedAt: 11111,
|
|
84
|
+
createdAt: 12345,
|
|
85
|
+
updatedAt: 67890,
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('validate() returns errors for empty fields', ({ assert }) => {
|
|
90
|
+
const model = new CustomDomainModel({
|
|
91
|
+
domain: '',
|
|
92
|
+
orgId: '',
|
|
93
|
+
dnsTarget: '',
|
|
94
|
+
})
|
|
95
|
+
const errors = model.validate()
|
|
96
|
+
assert.lengthOf(errors, 3)
|
|
97
|
+
assert.deepEqual(errors[0], { field: 'domain', message: 'Domain must not be empty', rule: 'notEmpty' })
|
|
98
|
+
assert.deepEqual(errors[1], { field: 'orgId', message: 'Org ID must not be empty', rule: 'notEmpty' })
|
|
99
|
+
assert.deepEqual(errors[2], { field: 'dnsTarget', message: 'DNS target must not be empty', rule: 'notEmpty' })
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('validate() returns no errors for valid model', ({ assert }) => {
|
|
103
|
+
const model = new CustomDomainModel({
|
|
104
|
+
domain: 'api.example.com',
|
|
105
|
+
orgId: 'org-1',
|
|
106
|
+
dnsTarget: 'target.example.com',
|
|
107
|
+
})
|
|
108
|
+
const errors = model.validate()
|
|
109
|
+
assert.lengthOf(errors, 0)
|
|
110
|
+
})
|
|
111
|
+
})
|