@api-client/core 0.14.2 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/index.d.ts +1 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +1 -0
- package/build/src/index.js.map +1 -1
- package/build/src/modeling/ApiFile.d.ts +23 -0
- package/build/src/modeling/ApiFile.d.ts.map +1 -0
- package/build/src/modeling/ApiFile.js +44 -0
- package/build/src/modeling/ApiFile.js.map +1 -0
- package/build/src/modeling/ApiModel.d.ts +159 -0
- package/build/src/modeling/ApiModel.d.ts.map +1 -0
- package/build/src/modeling/ApiModel.js +237 -0
- package/build/src/modeling/ApiModel.js.map +1 -0
- package/build/src/modeling/DataDomain.d.ts +1 -1
- package/build/src/modeling/DataDomain.d.ts.map +1 -1
- package/build/src/modeling/DataDomain.js +1 -3
- package/build/src/modeling/DataDomain.js.map +1 -1
- package/build/src/modeling/DomainEntity.js +1 -1
- package/build/src/modeling/DomainEntity.js.map +1 -1
- package/build/src/modeling/DomainFile.d.ts +1 -2
- package/build/src/modeling/DomainFile.d.ts.map +1 -1
- package/build/src/modeling/DomainFile.js +3 -41
- package/build/src/modeling/DomainFile.js.map +1 -1
- package/build/src/modeling/Semantics.d.ts +55 -8
- package/build/src/modeling/Semantics.d.ts.map +1 -1
- package/build/src/modeling/Semantics.js +62 -8
- package/build/src/modeling/Semantics.js.map +1 -1
- package/build/src/modeling/amf/ShapeGenerator.d.ts.map +1 -1
- package/build/src/modeling/amf/ShapeGenerator.js.map +1 -1
- package/build/src/modeling/types.d.ts +491 -0
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/src/models/kinds.d.ts +2 -0
- package/build/src/models/kinds.d.ts.map +1 -1
- package/build/src/models/kinds.js +2 -0
- package/build/src/models/kinds.js.map +1 -1
- package/build/src/models/store/File.d.ts +19 -2
- package/build/src/models/store/File.d.ts.map +1 -1
- package/build/src/models/store/File.js +100 -13
- package/build/src/models/store/File.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +19 -19
- package/package.json +2 -3
- package/src/modeling/ApiFile.ts +53 -0
- package/src/modeling/ApiModel.ts +327 -0
- package/src/modeling/DataDomain.ts +1 -1
- package/src/modeling/DomainEntity.ts +1 -1
- package/src/modeling/DomainFile.ts +3 -40
- package/src/modeling/Semantics.ts +63 -8
- package/src/modeling/amf/ShapeGenerator.ts +1 -1
- package/src/modeling/types.ts +545 -0
- package/src/models/kinds.ts +2 -0
- package/src/models/store/File.ts +100 -13
- package/tests/unit/modeling/api_model.spec.ts +291 -0
- package/tests/unit/modeling/domain_entity.spec.ts +15 -15
- package/tests/unit/modeling/domain_file.spec.ts +1 -11
- package/tests/unit/modeling/domain_model_entities.spec.ts +2 -2
- package/tests/unit/modeling/semantics.spec.ts +8 -11
- package/tests/unit/models/File/constructor.spec.ts +3 -2
- package/tests/unit/models/File/shortcutTo.spec.ts +1 -1
package/src/models/store/File.ts
CHANGED
|
@@ -181,6 +181,45 @@ export class StoredFile {
|
|
|
181
181
|
this[shortcutTargetSymbol] = value
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Creates a file schema object with the given properties.
|
|
186
|
+
* The `key` is generated if not set.
|
|
187
|
+
*
|
|
188
|
+
* @param input The input to create the file schema.
|
|
189
|
+
* @returns The file schema object.
|
|
190
|
+
*/
|
|
191
|
+
static createSchema(input: Partial<IStoredFile> = {}): IStoredFile {
|
|
192
|
+
const { key = nanoid(), kind, iconColor, shortcutTarget } = input
|
|
193
|
+
const result: IStoredFile = {
|
|
194
|
+
key,
|
|
195
|
+
kind: kind || '',
|
|
196
|
+
info: Thing.fromJSON(input.info, { name: 'Unnamed file' }).toJSON(),
|
|
197
|
+
}
|
|
198
|
+
if (iconColor) {
|
|
199
|
+
result.iconColor = iconColor
|
|
200
|
+
}
|
|
201
|
+
if (shortcutTarget) {
|
|
202
|
+
result.isShortcut = true
|
|
203
|
+
result.shortcutTarget = shortcutTarget
|
|
204
|
+
}
|
|
205
|
+
return result
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
constructor(state: IStoredFile) {
|
|
209
|
+
this.kind = state.kind
|
|
210
|
+
this.key = state.key
|
|
211
|
+
this.info = new Thing(state.info)
|
|
212
|
+
if (state.iconColor) {
|
|
213
|
+
this.iconColor = state.iconColor
|
|
214
|
+
}
|
|
215
|
+
if (state.shortcutTarget) {
|
|
216
|
+
this.shortcutTarget = state.shortcutTarget
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @deprecated Use `createSchema()` and setup values in the constructor instead.
|
|
222
|
+
*/
|
|
184
223
|
new(init: IStoredFile): this {
|
|
185
224
|
const { key = nanoid(), info, kind, iconColor, shortcutTarget } = init
|
|
186
225
|
this.key = key
|
|
@@ -409,6 +448,64 @@ export class File extends StoredFile {
|
|
|
409
448
|
return this[permissionsSymbol]
|
|
410
449
|
}
|
|
411
450
|
|
|
451
|
+
static override createSchema(input: Partial<IFile> = {}): IFile {
|
|
452
|
+
const result = StoredFile.createSchema(input) as IFile
|
|
453
|
+
if (Array.isArray(input.parents)) {
|
|
454
|
+
result.parents = [...input.parents]
|
|
455
|
+
} else {
|
|
456
|
+
result.parents = []
|
|
457
|
+
}
|
|
458
|
+
if (Array.isArray(input.permissionIds)) {
|
|
459
|
+
result.permissionIds = [...input.permissionIds]
|
|
460
|
+
} else {
|
|
461
|
+
result.permissionIds = []
|
|
462
|
+
}
|
|
463
|
+
if (Array.isArray(input.permissions)) {
|
|
464
|
+
result.permissions = input.permissions.map((i) => ({ ...i }))
|
|
465
|
+
} else {
|
|
466
|
+
result.permissions = []
|
|
467
|
+
}
|
|
468
|
+
if (input.capabilities) {
|
|
469
|
+
result.capabilities = { ...input.capabilities }
|
|
470
|
+
}
|
|
471
|
+
if (input.lastModified) {
|
|
472
|
+
result.lastModified = { ...input.lastModified }
|
|
473
|
+
} else {
|
|
474
|
+
result.lastModified = { user: '', time: 0, byMe: false }
|
|
475
|
+
}
|
|
476
|
+
if (typeof input.deleted === 'boolean') {
|
|
477
|
+
result.deleted = input.deleted
|
|
478
|
+
result.deletedInfo = input.deletedInfo ? { ...input.deletedInfo } : undefined
|
|
479
|
+
}
|
|
480
|
+
if (Array.isArray(input.labels)) {
|
|
481
|
+
result.labels = [...input.labels]
|
|
482
|
+
}
|
|
483
|
+
if (typeof input.shortcutTarget === 'string') {
|
|
484
|
+
result.shortcutTarget = input.shortcutTarget
|
|
485
|
+
}
|
|
486
|
+
return result
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
constructor(state?: Partial<IFile>) {
|
|
490
|
+
const init = File.createSchema(state)
|
|
491
|
+
super(init)
|
|
492
|
+
this[parentsSymbol] = [...init.parents]
|
|
493
|
+
this[permissionIdsSymbol] = [...init.permissionIds]
|
|
494
|
+
this[permissionsSymbol] = init.permissions.map((i) => ({ ...i }))
|
|
495
|
+
this[capabilitiesSymbol] = init.capabilities ? { ...init.capabilities } : undefined
|
|
496
|
+
this[lastModifiedSymbol] = Object.freeze({ ...init.lastModified })
|
|
497
|
+
if (typeof init.deleted === 'boolean') {
|
|
498
|
+
this[deletedSymbol] = init.deleted
|
|
499
|
+
this[deletedInfoSymbol] = init.deletedInfo ? Object.freeze({ ...init.deletedInfo }) : undefined
|
|
500
|
+
}
|
|
501
|
+
if (Array.isArray(init.labels)) {
|
|
502
|
+
this.labels = [...init.labels]
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @deprecated Use `createSchema()` and setup values in the constructor instead.
|
|
508
|
+
*/
|
|
412
509
|
override new(init: IFile): this {
|
|
413
510
|
super.new(init)
|
|
414
511
|
const { permissions = [], parents = [], permissionIds = [], deleted, deletedInfo, lastModified, labels } = init
|
|
@@ -453,20 +550,10 @@ export class File extends StoredFile {
|
|
|
453
550
|
|
|
454
551
|
/**
|
|
455
552
|
* @param name The name to set.
|
|
456
|
-
* @param
|
|
553
|
+
* @param kind The kind of the file to create.
|
|
457
554
|
*/
|
|
458
|
-
static fromName(name: string, kind
|
|
459
|
-
const
|
|
460
|
-
const definition = new File()
|
|
461
|
-
definition.new({
|
|
462
|
-
key,
|
|
463
|
-
kind,
|
|
464
|
-
info: Thing.fromName(name).toJSON(),
|
|
465
|
-
parents: [],
|
|
466
|
-
permissionIds: [],
|
|
467
|
-
permissions: [],
|
|
468
|
-
lastModified: { user: '', time: 0, byMe: false },
|
|
469
|
-
})
|
|
555
|
+
static fromName(name: string, kind: string): File {
|
|
556
|
+
const definition = new File({ kind, info: { name } })
|
|
470
557
|
return definition
|
|
471
558
|
}
|
|
472
559
|
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
ApiModel,
|
|
4
|
+
ApiModelKind,
|
|
5
|
+
DataDomain,
|
|
6
|
+
type RolesBasedAccessControl,
|
|
7
|
+
type ApiModelSchema,
|
|
8
|
+
type ExposedEntity,
|
|
9
|
+
} from '../../../src/index.js'
|
|
10
|
+
|
|
11
|
+
test.group('ApiModel.createSchema()', () => {
|
|
12
|
+
test('creates a schema with default values', ({ assert }) => {
|
|
13
|
+
const schema = ApiModel.createSchema()
|
|
14
|
+
assert.equal(schema.kind, ApiModelKind)
|
|
15
|
+
assert.typeOf(schema.key, 'string')
|
|
16
|
+
assert.isNotEmpty(schema.key)
|
|
17
|
+
assert.deepInclude(schema.info, { name: 'Unnamed API' })
|
|
18
|
+
assert.deepEqual(schema.exposes, [])
|
|
19
|
+
assert.isUndefined(schema.userKey)
|
|
20
|
+
assert.isUndefined(schema.domain)
|
|
21
|
+
assert.isUndefined(schema.authentication)
|
|
22
|
+
assert.isUndefined(schema.authorization)
|
|
23
|
+
assert.isUndefined(schema.session)
|
|
24
|
+
assert.isUndefined(schema.accessRule)
|
|
25
|
+
assert.isUndefined(schema.rateLimiting)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('creates a schema with provided values', ({ assert }) => {
|
|
29
|
+
const input: Partial<ApiModelSchema> = {
|
|
30
|
+
key: 'test-api',
|
|
31
|
+
info: { name: 'Test API', description: 'A test API' },
|
|
32
|
+
exposes: [{ key: 'entity1', actions: [] }],
|
|
33
|
+
userKey: 'user-entity',
|
|
34
|
+
domain: { key: 'domain1', version: '1.0.0' },
|
|
35
|
+
authentication: { strategy: 'UsernamePassword' },
|
|
36
|
+
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
37
|
+
session: { secret: 'secret', properties: ['email'] },
|
|
38
|
+
accessRule: [{ type: 'public' }],
|
|
39
|
+
rateLimiting: { rules: [] },
|
|
40
|
+
}
|
|
41
|
+
const schema = ApiModel.createSchema(input)
|
|
42
|
+
|
|
43
|
+
assert.equal(schema.kind, ApiModelKind)
|
|
44
|
+
assert.equal(schema.key, 'test-api')
|
|
45
|
+
assert.deepInclude(schema.info, { name: 'Test API', description: 'A test API' })
|
|
46
|
+
assert.deepEqual(schema.exposes, [{ key: 'entity1', actions: [] }])
|
|
47
|
+
assert.equal(schema.userKey, 'user-entity')
|
|
48
|
+
assert.deepEqual(schema.domain, { key: 'domain1', version: '1.0.0' })
|
|
49
|
+
assert.deepEqual(schema.authentication, { strategy: 'UsernamePassword' })
|
|
50
|
+
assert.deepEqual(schema.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
51
|
+
assert.deepEqual(schema.session, { secret: 'secret', properties: ['email'] })
|
|
52
|
+
assert.deepEqual(schema.accessRule, [{ type: 'public' }])
|
|
53
|
+
assert.deepEqual(schema.rateLimiting, { rules: [] })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('creates a schema with partial info', ({ assert }) => {
|
|
57
|
+
const schema = ApiModel.createSchema({ info: { name: 'Partial API' } })
|
|
58
|
+
assert.deepInclude(schema.info, { name: 'Partial API' })
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('creates a schema with empty info', ({ assert }) => {
|
|
62
|
+
const schema = ApiModel.createSchema({ info: {} })
|
|
63
|
+
assert.deepInclude(schema.info, { name: 'Unnamed API' })
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test.group('ApiModel.constructor()', () => {
|
|
68
|
+
test('creates an instance with default values', ({ assert }) => {
|
|
69
|
+
const model = new ApiModel()
|
|
70
|
+
assert.equal(model.kind, ApiModelKind)
|
|
71
|
+
assert.typeOf(model.key, 'string')
|
|
72
|
+
assert.isNotEmpty(model.key)
|
|
73
|
+
assert.equal(model.info.name, 'Unnamed API')
|
|
74
|
+
assert.deepEqual(model.exposes, [])
|
|
75
|
+
assert.isUndefined(model.userKey)
|
|
76
|
+
assert.isUndefined(model.domain)
|
|
77
|
+
assert.isUndefined(model.authentication)
|
|
78
|
+
assert.isUndefined(model.authorization)
|
|
79
|
+
assert.isUndefined(model.session)
|
|
80
|
+
assert.isUndefined(model.accessRule)
|
|
81
|
+
assert.isUndefined(model.rateLimiting)
|
|
82
|
+
assert.isUndefined(model.dataDomain)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('creates an instance with provided schema values', ({ assert }) => {
|
|
86
|
+
const schema: ApiModelSchema = {
|
|
87
|
+
kind: ApiModelKind,
|
|
88
|
+
key: 'test-api',
|
|
89
|
+
info: { name: 'Test API', description: 'A test API' },
|
|
90
|
+
exposes: [{ key: 'entity1', actions: [] }],
|
|
91
|
+
userKey: 'user-entity',
|
|
92
|
+
domain: { key: 'domain1', version: '1.0.0' },
|
|
93
|
+
authentication: { strategy: 'UsernamePassword' },
|
|
94
|
+
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
95
|
+
session: { secret: 'secret', properties: ['email'] },
|
|
96
|
+
accessRule: [{ type: 'public' }],
|
|
97
|
+
rateLimiting: { rules: [] },
|
|
98
|
+
}
|
|
99
|
+
const model = new ApiModel(schema)
|
|
100
|
+
|
|
101
|
+
assert.equal(model.key, 'test-api')
|
|
102
|
+
assert.equal(model.info.name, 'Test API')
|
|
103
|
+
assert.deepEqual(model.exposes, [{ key: 'entity1', actions: [] }])
|
|
104
|
+
assert.equal(model.userKey, 'user-entity')
|
|
105
|
+
assert.deepEqual(model.domain, { key: 'domain1', version: '1.0.0' })
|
|
106
|
+
assert.deepEqual(model.authentication, { strategy: 'UsernamePassword' })
|
|
107
|
+
assert.deepEqual(model.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
108
|
+
assert.deepEqual(model.session, { secret: 'secret', properties: ['email'] })
|
|
109
|
+
assert.deepEqual(model.accessRule, [{ type: 'public' }])
|
|
110
|
+
assert.deepEqual(model.rateLimiting, { rules: [] })
|
|
111
|
+
assert.isUndefined(model.dataDomain)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('creates an instance with a DataDomain', ({ assert }) => {
|
|
115
|
+
const domainSchema = DataDomain.createSchema({ key: 'my-domain' })
|
|
116
|
+
const model = new ApiModel({}, domainSchema)
|
|
117
|
+
assert.isDefined(model.dataDomain)
|
|
118
|
+
assert.instanceOf(model.dataDomain, DataDomain)
|
|
119
|
+
assert.equal(model.dataDomain!.key, 'my-domain')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('notifies change when info is modified', async ({ assert }) => {
|
|
123
|
+
const model = new ApiModel()
|
|
124
|
+
let notified = false
|
|
125
|
+
model.addEventListener('change', () => {
|
|
126
|
+
notified = true
|
|
127
|
+
})
|
|
128
|
+
model.info.name = 'New Name'
|
|
129
|
+
await Promise.resolve() // Allow microtask to run
|
|
130
|
+
assert.isTrue(notified)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test.group('ApiModel.toJSON()', () => {
|
|
135
|
+
test('serializes default values', ({ assert }) => {
|
|
136
|
+
const model = new ApiModel()
|
|
137
|
+
const json = model.toJSON()
|
|
138
|
+
|
|
139
|
+
assert.equal(json.kind, ApiModelKind)
|
|
140
|
+
assert.equal(json.key, model.key)
|
|
141
|
+
assert.deepInclude(json.info, { name: 'Unnamed API' })
|
|
142
|
+
assert.deepEqual(json.exposes, [])
|
|
143
|
+
assert.isUndefined(json.userKey)
|
|
144
|
+
assert.isUndefined(json.domain)
|
|
145
|
+
assert.isUndefined(json.authentication)
|
|
146
|
+
assert.isUndefined(json.authorization)
|
|
147
|
+
assert.isUndefined(json.session)
|
|
148
|
+
assert.isUndefined(json.accessRule)
|
|
149
|
+
assert.isUndefined(json.rateLimiting)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('serializes all provided values', ({ assert }) => {
|
|
153
|
+
const schema: ApiModelSchema = {
|
|
154
|
+
kind: ApiModelKind,
|
|
155
|
+
key: 'test-api',
|
|
156
|
+
info: { name: 'Test API', description: 'A test API' },
|
|
157
|
+
exposes: [{ key: 'entity1', actions: [] }],
|
|
158
|
+
userKey: 'user-entity',
|
|
159
|
+
domain: { key: 'domain1', version: '1.0.0' },
|
|
160
|
+
authentication: { strategy: 'UsernamePassword' },
|
|
161
|
+
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
162
|
+
session: { secret: 'secret', properties: ['email'] },
|
|
163
|
+
accessRule: [{ type: 'public' }],
|
|
164
|
+
rateLimiting: { rules: [] },
|
|
165
|
+
}
|
|
166
|
+
const model = new ApiModel(schema)
|
|
167
|
+
const json = model.toJSON()
|
|
168
|
+
|
|
169
|
+
assert.equal(json.key, 'test-api')
|
|
170
|
+
assert.deepInclude(json.info, { name: 'Test API', description: 'A test API' })
|
|
171
|
+
assert.deepEqual(json.exposes, [{ key: 'entity1', actions: [] }])
|
|
172
|
+
assert.equal(json.userKey, 'user-entity')
|
|
173
|
+
assert.deepEqual(json.domain, { key: 'domain1', version: '1.0.0' })
|
|
174
|
+
assert.deepEqual(json.authentication, { strategy: 'UsernamePassword' })
|
|
175
|
+
assert.deepEqual(json.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
176
|
+
assert.deepEqual(json.session, { secret: 'secret', properties: ['email'] })
|
|
177
|
+
assert.deepEqual(json.accessRule, [{ type: 'public' }])
|
|
178
|
+
assert.deepEqual(json.rateLimiting, { rules: [] })
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
test.group('ApiModel.exposeEntity()', () => {
|
|
183
|
+
test('exposes a new entity', ({ assert }) => {
|
|
184
|
+
const model = new ApiModel()
|
|
185
|
+
const entityKey = 'new-entity'
|
|
186
|
+
const exposedEntity = model.exposeEntity(entityKey)
|
|
187
|
+
|
|
188
|
+
assert.isDefined(exposedEntity)
|
|
189
|
+
assert.equal(exposedEntity.key, entityKey)
|
|
190
|
+
assert.deepEqual(exposedEntity.actions, [])
|
|
191
|
+
assert.includeDeepMembers(model.exposes, [exposedEntity])
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('returns an existing entity if already exposed', ({ assert }) => {
|
|
195
|
+
const model = new ApiModel()
|
|
196
|
+
const entityKey = 'existing-entity'
|
|
197
|
+
const initialExposedEntity = model.exposeEntity(entityKey)
|
|
198
|
+
const retrievedExposedEntity = model.exposeEntity(entityKey)
|
|
199
|
+
|
|
200
|
+
assert.strictEqual(retrievedExposedEntity, initialExposedEntity)
|
|
201
|
+
assert.lengthOf(model.exposes, 1)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('notifies change when a new entity is exposed', async ({ assert }) => {
|
|
205
|
+
const model = new ApiModel()
|
|
206
|
+
let notified = false
|
|
207
|
+
model.addEventListener('change', () => {
|
|
208
|
+
notified = true
|
|
209
|
+
})
|
|
210
|
+
model.exposeEntity('notify-entity')
|
|
211
|
+
await Promise.resolve() // Allow microtask to run
|
|
212
|
+
assert.isTrue(notified)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('does not notify change if entity already exposed', async ({ assert }) => {
|
|
216
|
+
const model = new ApiModel()
|
|
217
|
+
model.exposeEntity('no-notify-entity') // First exposure
|
|
218
|
+
await Promise.resolve() // Allow microtask to run
|
|
219
|
+
let notified = false
|
|
220
|
+
model.addEventListener('change', () => {
|
|
221
|
+
notified = true
|
|
222
|
+
})
|
|
223
|
+
model.exposeEntity('no-notify-entity') // Second exposure
|
|
224
|
+
await Promise.resolve() // Allow microtask to run
|
|
225
|
+
assert.isFalse(notified)
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
test.group('ApiModel.removeEntity()', () => {
|
|
230
|
+
test('removes an existing entity', ({ assert }) => {
|
|
231
|
+
const model = new ApiModel()
|
|
232
|
+
const entityKey = 'entity-to-remove'
|
|
233
|
+
model.exposeEntity(entityKey)
|
|
234
|
+
assert.lengthOf(model.exposes, 1)
|
|
235
|
+
|
|
236
|
+
model.removeEntity(entityKey)
|
|
237
|
+
assert.lengthOf(model.exposes, 0)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('does nothing if entity does not exist', ({ assert }) => {
|
|
241
|
+
const model = new ApiModel()
|
|
242
|
+
model.exposeEntity('existing-entity')
|
|
243
|
+
const initialExposes = [...model.exposes]
|
|
244
|
+
|
|
245
|
+
model.removeEntity('non-existing-entity')
|
|
246
|
+
assert.deepEqual(model.exposes, initialExposes)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('notifies change when an entity is removed', async ({ assert }) => {
|
|
250
|
+
const model = new ApiModel()
|
|
251
|
+
const entityKey = 'notify-remove-entity'
|
|
252
|
+
model.exposeEntity(entityKey)
|
|
253
|
+
|
|
254
|
+
let notified = false
|
|
255
|
+
model.addEventListener('change', () => {
|
|
256
|
+
notified = true
|
|
257
|
+
})
|
|
258
|
+
model.removeEntity(entityKey)
|
|
259
|
+
await Promise.resolve() // Allow microtask to run
|
|
260
|
+
assert.isTrue(notified)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('does not notify change if entity to remove does not exist', async ({ assert }) => {
|
|
264
|
+
const model = new ApiModel()
|
|
265
|
+
let notified = false
|
|
266
|
+
model.addEventListener('change', () => {
|
|
267
|
+
notified = true
|
|
268
|
+
})
|
|
269
|
+
model.removeEntity('no-notify-remove-entity')
|
|
270
|
+
await Promise.resolve() // Allow microtask to run
|
|
271
|
+
assert.isFalse(notified)
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test.group('ApiModel.getExposedEntity()', () => {
|
|
276
|
+
test('returns an existing exposed entity', ({ assert }) => {
|
|
277
|
+
const model = new ApiModel()
|
|
278
|
+
const entityKey = 'get-entity'
|
|
279
|
+
const exposed: ExposedEntity = { key: entityKey, actions: [] }
|
|
280
|
+
model.exposes.push(exposed)
|
|
281
|
+
|
|
282
|
+
const retrievedEntity = model.getExposedEntity(entityKey)
|
|
283
|
+
assert.deepEqual(retrievedEntity, exposed)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
test('returns undefined if entity is not exposed', ({ assert }) => {
|
|
287
|
+
const model = new ApiModel()
|
|
288
|
+
const retrievedEntity = model.getExposedEntity('non-exposed-entity')
|
|
289
|
+
assert.isUndefined(retrievedEntity)
|
|
290
|
+
})
|
|
291
|
+
})
|
|
@@ -17,7 +17,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
17
17
|
assert.equal(schema.kind, DomainEntityKind)
|
|
18
18
|
assert.typeOf(schema.key, 'string')
|
|
19
19
|
assert.isNotEmpty(schema.key)
|
|
20
|
-
assert.deepInclude(schema.info, { name: '
|
|
20
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
21
21
|
assert.isUndefined(schema.tags)
|
|
22
22
|
assert.isUndefined(schema.semantics)
|
|
23
23
|
assert.isUndefined(schema.fields)
|
|
@@ -59,7 +59,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
59
59
|
assert.equal(schema.kind, DomainEntityKind)
|
|
60
60
|
assert.typeOf(schema.key, 'string')
|
|
61
61
|
assert.isNotEmpty(schema.key)
|
|
62
|
-
assert.deepInclude(schema.info, { name: '
|
|
62
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
63
63
|
})
|
|
64
64
|
|
|
65
65
|
test('creates a schema with tags', ({ assert }) => {
|
|
@@ -69,7 +69,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
69
69
|
assert.equal(schema.kind, DomainEntityKind)
|
|
70
70
|
assert.typeOf(schema.key, 'string')
|
|
71
71
|
assert.isNotEmpty(schema.key)
|
|
72
|
-
assert.deepInclude(schema.info, { name: '
|
|
72
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
73
73
|
assert.deepEqual(schema.tags, ['tag1', 'tag2'])
|
|
74
74
|
})
|
|
75
75
|
|
|
@@ -80,7 +80,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
80
80
|
assert.equal(schema.kind, DomainEntityKind)
|
|
81
81
|
assert.typeOf(schema.key, 'string')
|
|
82
82
|
assert.isNotEmpty(schema.key)
|
|
83
|
-
assert.deepInclude(schema.info, { name: '
|
|
83
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
84
84
|
assert.deepEqual(schema.semantics, [{ id: SemanticType.User }])
|
|
85
85
|
})
|
|
86
86
|
|
|
@@ -91,7 +91,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
91
91
|
assert.equal(schema.kind, DomainEntityKind)
|
|
92
92
|
assert.typeOf(schema.key, 'string')
|
|
93
93
|
assert.isNotEmpty(schema.key)
|
|
94
|
-
assert.deepInclude(schema.info, { name: '
|
|
94
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
95
95
|
assert.deepEqual(schema.fields, [{ key: 'test-property', type: 'property' }])
|
|
96
96
|
})
|
|
97
97
|
|
|
@@ -102,7 +102,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
102
102
|
assert.equal(schema.kind, DomainEntityKind)
|
|
103
103
|
assert.typeOf(schema.key, 'string')
|
|
104
104
|
assert.isNotEmpty(schema.key)
|
|
105
|
-
assert.deepInclude(schema.info, { name: '
|
|
105
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
106
106
|
assert.isTrue(schema.deprecated)
|
|
107
107
|
})
|
|
108
108
|
|
|
@@ -113,7 +113,7 @@ test.group('DomainEntity.createSchema()', () => {
|
|
|
113
113
|
assert.equal(schema.kind, DomainEntityKind)
|
|
114
114
|
assert.typeOf(schema.key, 'string')
|
|
115
115
|
assert.isNotEmpty(schema.key)
|
|
116
|
-
assert.deepInclude(schema.info, { name: '
|
|
116
|
+
assert.deepInclude(schema.info, { name: 'new_entity' })
|
|
117
117
|
assert.isFalse(schema.deprecated)
|
|
118
118
|
})
|
|
119
119
|
|
|
@@ -164,7 +164,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
164
164
|
assert.typeOf(entity.key, 'string')
|
|
165
165
|
assert.isNotEmpty(entity.key)
|
|
166
166
|
assert.instanceOf(entity.info, Thing)
|
|
167
|
-
assert.equal(entity.info.name, '
|
|
167
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
168
168
|
assert.deepEqual(entity.tags, [])
|
|
169
169
|
assert.deepEqual(entity.semantics, [])
|
|
170
170
|
assert.deepEqual(entity.fields, [])
|
|
@@ -213,7 +213,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
213
213
|
assert.typeOf(entity.key, 'string')
|
|
214
214
|
assert.isNotEmpty(entity.key)
|
|
215
215
|
assert.instanceOf(entity.info, Thing)
|
|
216
|
-
assert.equal(entity.info.name, '
|
|
216
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
217
217
|
})
|
|
218
218
|
|
|
219
219
|
test('creates a new DomainEntity with tags', ({ assert }) => {
|
|
@@ -225,7 +225,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
225
225
|
assert.typeOf(entity.key, 'string')
|
|
226
226
|
assert.isNotEmpty(entity.key)
|
|
227
227
|
assert.instanceOf(entity.info, Thing)
|
|
228
|
-
assert.equal(entity.info.name, '
|
|
228
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
229
229
|
assert.deepEqual(entity.tags, ['tag1', 'tag2'])
|
|
230
230
|
})
|
|
231
231
|
|
|
@@ -238,7 +238,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
238
238
|
assert.typeOf(entity.key, 'string')
|
|
239
239
|
assert.isNotEmpty(entity.key)
|
|
240
240
|
assert.instanceOf(entity.info, Thing)
|
|
241
|
-
assert.equal(entity.info.name, '
|
|
241
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
242
242
|
assert.deepEqual(entity.semantics, [{ id: SemanticType.User }])
|
|
243
243
|
})
|
|
244
244
|
|
|
@@ -251,7 +251,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
251
251
|
assert.typeOf(entity.key, 'string')
|
|
252
252
|
assert.isNotEmpty(entity.key)
|
|
253
253
|
assert.instanceOf(entity.info, Thing)
|
|
254
|
-
assert.equal(entity.info.name, '
|
|
254
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
255
255
|
assert.deepEqual(entity.fields, [{ key: 'test-property', type: 'property' }])
|
|
256
256
|
})
|
|
257
257
|
|
|
@@ -264,7 +264,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
264
264
|
assert.typeOf(entity.key, 'string')
|
|
265
265
|
assert.isNotEmpty(entity.key)
|
|
266
266
|
assert.instanceOf(entity.info, Thing)
|
|
267
|
-
assert.equal(entity.info.name, '
|
|
267
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
268
268
|
assert.isTrue(entity.deprecated)
|
|
269
269
|
})
|
|
270
270
|
|
|
@@ -277,7 +277,7 @@ test.group('DomainEntity.constructor()', () => {
|
|
|
277
277
|
assert.typeOf(entity.key, 'string')
|
|
278
278
|
assert.isNotEmpty(entity.key)
|
|
279
279
|
assert.instanceOf(entity.info, Thing)
|
|
280
|
-
assert.equal(entity.info.name, '
|
|
280
|
+
assert.equal(entity.info.name, 'new_entity')
|
|
281
281
|
assert.isFalse(entity.deprecated)
|
|
282
282
|
})
|
|
283
283
|
|
|
@@ -350,7 +350,7 @@ test.group('DomainEntity.toJSON()', () => {
|
|
|
350
350
|
const json = entity.toJSON()
|
|
351
351
|
assert.equal(json.kind, DomainEntityKind)
|
|
352
352
|
assert.equal(json.key, entity.key)
|
|
353
|
-
assert.deepInclude(json.info, { name: '
|
|
353
|
+
assert.deepInclude(json.info, { name: 'new_entity' })
|
|
354
354
|
assert.isUndefined(json.tags)
|
|
355
355
|
assert.isUndefined(json.semantics)
|
|
356
356
|
assert.isUndefined(json.fields)
|
|
@@ -99,22 +99,12 @@ test.group('constructor()', () => {
|
|
|
99
99
|
],
|
|
100
100
|
lastModified: { byMe: false, time: 0, user: 'id', name: 'test' },
|
|
101
101
|
}
|
|
102
|
-
const result = new DomainFile(
|
|
102
|
+
const result = new DomainFile(schema)
|
|
103
103
|
assert.equal(result.kind, DomainFileKind)
|
|
104
104
|
assert.equal(result.info.name, 'hello')
|
|
105
105
|
assert.equal(result.key, '123')
|
|
106
106
|
assert.deepEqual(result.lastModified, schema.lastModified)
|
|
107
107
|
})
|
|
108
|
-
|
|
109
|
-
test('throws when invalid schema', ({ assert }) => {
|
|
110
|
-
assert.throws(() => {
|
|
111
|
-
new DomainFile(
|
|
112
|
-
JSON.stringify({
|
|
113
|
-
name: 'a name',
|
|
114
|
-
})
|
|
115
|
-
)
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
108
|
})
|
|
119
109
|
|
|
120
110
|
test.group('toJSON()', () => {
|
|
@@ -9,7 +9,7 @@ test.group('DomainModel.addEntity()', () => {
|
|
|
9
9
|
assert.instanceOf(entity, DomainEntity)
|
|
10
10
|
assert.equal(entity.kind, DomainEntityKind)
|
|
11
11
|
assert.equal(entity.key, 'test-entity')
|
|
12
|
-
assert.deepInclude(entity.info, { name: '
|
|
12
|
+
assert.deepInclude(entity.info, { name: 'new_entity' })
|
|
13
13
|
assert.isTrue(dataDomain.graph.hasNode(entity.key))
|
|
14
14
|
assert.equal(dataDomain.graph.parent(entity.key), model.key)
|
|
15
15
|
})
|
|
@@ -22,7 +22,7 @@ test.group('DomainModel.addEntity()', () => {
|
|
|
22
22
|
assert.equal(entity.kind, DomainEntityKind)
|
|
23
23
|
assert.typeOf(entity.key, 'string')
|
|
24
24
|
assert.isNotEmpty(entity.key)
|
|
25
|
-
assert.deepInclude(entity.info, { name: '
|
|
25
|
+
assert.deepInclude(entity.info, { name: 'new_entity' })
|
|
26
26
|
assert.isTrue(dataDomain.graph.hasNode(entity.key))
|
|
27
27
|
assert.equal(dataDomain.graph.parent(entity.key), model.key)
|
|
28
28
|
})
|
|
@@ -14,17 +14,14 @@ import {
|
|
|
14
14
|
|
|
15
15
|
test.group('Semantics', () => {
|
|
16
16
|
test('SemanticType enum should have correct values', ({ assert }) => {
|
|
17
|
-
assert.equal(SemanticType.User, '
|
|
18
|
-
assert.equal(SemanticType.CreatedTimestamp, '
|
|
19
|
-
assert.equal(SemanticType.UpdatedTimestamp, '
|
|
20
|
-
assert.equal(SemanticType.DeletedTimestamp, '
|
|
21
|
-
assert.equal(SemanticType.DeletedFlag, '
|
|
22
|
-
assert.equal(SemanticType.PublicUniqueName, '
|
|
23
|
-
assert.equal(SemanticType.UserRole, '
|
|
24
|
-
assert.equal(
|
|
25
|
-
SemanticType.ResourceOwnerIdentifier,
|
|
26
|
-
'https://apinow.app/semantics/associations/#ResourceOwnerIdentifier'
|
|
27
|
-
)
|
|
17
|
+
assert.equal(SemanticType.User, 'Semantic#User')
|
|
18
|
+
assert.equal(SemanticType.CreatedTimestamp, 'Semantic#CreatedTimestamp')
|
|
19
|
+
assert.equal(SemanticType.UpdatedTimestamp, 'Semantic#UpdatedTimestamp')
|
|
20
|
+
assert.equal(SemanticType.DeletedTimestamp, 'Semantic#DeletedTimestamp')
|
|
21
|
+
assert.equal(SemanticType.DeletedFlag, 'Semantic#DeletedFlag')
|
|
22
|
+
assert.equal(SemanticType.PublicUniqueName, 'Semantic#PublicUniqueName')
|
|
23
|
+
assert.equal(SemanticType.UserRole, 'Semantic#UserRole')
|
|
24
|
+
assert.equal(SemanticType.ResourceOwnerIdentifier, 'Semantic#ResourceOwnerIdentifier')
|
|
28
25
|
})
|
|
29
26
|
|
|
30
27
|
test('SemanticScope enum should have correct values', ({ assert }) => {
|
|
@@ -5,8 +5,9 @@ test.group('File.constructor()', () => {
|
|
|
5
5
|
test('creates a default File', ({ assert }) => {
|
|
6
6
|
const result = new File()
|
|
7
7
|
assert.equal(result.kind, '')
|
|
8
|
-
assert.
|
|
9
|
-
assert.
|
|
8
|
+
assert.isString(result.key, 'has the key')
|
|
9
|
+
assert.isNotEmpty(result.key, 'key is not empty')
|
|
10
|
+
assert.deepEqual(result.info.toJSON(), { kind: ThingKind, name: 'Unnamed file' })
|
|
10
11
|
assert.deepEqual(result.parents, [])
|
|
11
12
|
assert.deepEqual(result.permissionIds, [])
|
|
12
13
|
assert.isFalse(result.deleted, 'deleted')
|
|
@@ -46,7 +46,7 @@ test.group('File.shortcutTo()', () => {
|
|
|
46
46
|
})
|
|
47
47
|
|
|
48
48
|
test('creates a shortcut from a name with fromName()', ({ assert }) => {
|
|
49
|
-
const file = File.fromName('test-file')
|
|
49
|
+
const file = File.fromName('test-file', 'shortcut')
|
|
50
50
|
const targetId = 'target-file-id'
|
|
51
51
|
file.shortcutTo(targetId)
|
|
52
52
|
assert.equal(file.shortcutTarget, targetId)
|