@api-client/core 0.19.1 → 0.19.2
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/mocking/ModelingMock.d.ts +2 -0
- package/build/src/mocking/ModelingMock.d.ts.map +1 -1
- package/build/src/mocking/ModelingMock.js +2 -0
- package/build/src/mocking/ModelingMock.js.map +1 -1
- package/build/src/mocking/lib/Ai.d.ts +11 -0
- package/build/src/mocking/lib/Ai.d.ts.map +1 -0
- package/build/src/mocking/lib/Ai.js +53 -0
- package/build/src/mocking/lib/Ai.js.map +1 -0
- package/build/src/modeling/ai/DataDomainDelta.d.ts +146 -0
- package/build/src/modeling/ai/DataDomainDelta.d.ts.map +1 -0
- package/build/src/modeling/ai/DataDomainDelta.js +729 -0
- package/build/src/modeling/ai/DataDomainDelta.js.map +1 -0
- package/build/src/modeling/ai/DomainSerialization.d.ts +20 -0
- package/build/src/modeling/ai/DomainSerialization.d.ts.map +1 -0
- package/build/src/modeling/ai/DomainSerialization.js +185 -0
- package/build/src/modeling/ai/DomainSerialization.js.map +1 -0
- package/build/src/modeling/ai/domain_response_schema.d.ts +806 -0
- package/build/src/modeling/ai/domain_response_schema.d.ts.map +1 -0
- package/build/src/modeling/ai/domain_response_schema.js +289 -0
- package/build/src/modeling/ai/domain_response_schema.js.map +1 -0
- package/build/src/modeling/ai/domain_tools.d.ts +68 -0
- package/build/src/modeling/ai/domain_tools.d.ts.map +1 -0
- package/build/src/modeling/ai/domain_tools.js +71 -0
- package/build/src/modeling/ai/domain_tools.js.map +1 -0
- package/build/src/modeling/ai/index.d.ts +10 -0
- package/build/src/modeling/ai/index.d.ts.map +1 -0
- package/build/src/modeling/ai/index.js +9 -0
- package/build/src/modeling/ai/index.js.map +1 -0
- package/build/src/modeling/ai/message_parser.d.ts +23 -0
- package/build/src/modeling/ai/message_parser.d.ts.map +1 -0
- package/build/src/modeling/ai/message_parser.js +93 -0
- package/build/src/modeling/ai/message_parser.js.map +1 -0
- package/build/src/modeling/ai/prompts/domain_system.d.ts +6 -0
- package/build/src/modeling/ai/prompts/domain_system.d.ts.map +1 -0
- package/build/src/modeling/ai/prompts/domain_system.js +80 -0
- package/build/src/modeling/ai/prompts/domain_system.js.map +1 -0
- package/build/src/modeling/ai/tools/DataDomain.tools.d.ts +25 -0
- package/build/src/modeling/ai/tools/DataDomain.tools.d.ts.map +1 -0
- package/build/src/modeling/ai/tools/DataDomain.tools.js +334 -0
- package/build/src/modeling/ai/tools/DataDomain.tools.js.map +1 -0
- package/build/src/modeling/ai/tools/Semantic.tools.d.ts +48 -0
- package/build/src/modeling/ai/tools/Semantic.tools.d.ts.map +1 -0
- package/build/src/modeling/ai/tools/Semantic.tools.js +36 -0
- package/build/src/modeling/ai/tools/Semantic.tools.js.map +1 -0
- package/build/src/modeling/ai/tools/config.d.ts +13 -0
- package/build/src/modeling/ai/tools/config.d.ts.map +1 -0
- package/build/src/modeling/ai/tools/config.js +2 -0
- package/build/src/modeling/ai/tools/config.js.map +1 -0
- package/build/src/modeling/ai/types.d.ts +302 -0
- package/build/src/modeling/ai/types.d.ts.map +1 -0
- package/build/src/modeling/ai/types.js +40 -0
- package/build/src/modeling/ai/types.js.map +1 -0
- package/build/src/models/AiMessage.d.ts +185 -0
- package/build/src/models/AiMessage.d.ts.map +1 -0
- package/build/src/models/AiMessage.js +203 -0
- package/build/src/models/AiMessage.js.map +1 -0
- package/build/src/models/AiSession.d.ts +80 -0
- package/build/src/models/AiSession.d.ts.map +1 -0
- package/build/src/models/AiSession.js +102 -0
- package/build/src/models/AiSession.js.map +1 -0
- 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/sdk/AiSdk.d.ts +93 -0
- package/build/src/sdk/AiSdk.d.ts.map +1 -0
- package/build/src/sdk/AiSdk.js +348 -0
- package/build/src/sdk/AiSdk.js.map +1 -0
- package/build/src/sdk/RouteBuilder.d.ts +7 -0
- package/build/src/sdk/RouteBuilder.d.ts.map +1 -1
- package/build/src/sdk/RouteBuilder.js +18 -0
- package/build/src/sdk/RouteBuilder.js.map +1 -1
- package/build/src/sdk/Sdk.d.ts +2 -0
- package/build/src/sdk/Sdk.d.ts.map +1 -1
- package/build/src/sdk/Sdk.js +2 -0
- package/build/src/sdk/Sdk.js.map +1 -1
- package/build/src/sdk/SdkBase.d.ts +4 -0
- package/build/src/sdk/SdkBase.d.ts.map +1 -1
- package/build/src/sdk/SdkBase.js.map +1 -1
- package/build/src/sdk/SdkMock.d.ts +15 -0
- package/build/src/sdk/SdkMock.d.ts.map +1 -1
- package/build/src/sdk/SdkMock.js +118 -0
- package/build/src/sdk/SdkMock.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +22 -22
- package/package.json +3 -3
- package/src/mocking/ModelingMock.ts +2 -0
- package/src/mocking/lib/Ai.ts +71 -0
- package/src/modeling/ai/DataDomainDelta.ts +798 -0
- package/src/modeling/ai/DomainSerialization.ts +199 -0
- package/src/modeling/ai/domain_response_schema.ts +301 -0
- package/src/modeling/ai/domain_tools.ts +76 -0
- package/src/modeling/ai/message_parser.ts +101 -0
- package/src/modeling/ai/prompts/domain_system.ts +79 -0
- package/src/modeling/ai/readme.md +8 -0
- package/src/modeling/ai/tools/DataDomain.tools.ts +365 -0
- package/src/modeling/ai/tools/Semantic.tools.ts +38 -0
- package/src/modeling/ai/tools/config.ts +13 -0
- package/src/modeling/ai/tools/readme.md +3 -0
- package/src/modeling/ai/types.ts +306 -0
- package/src/models/AiMessage.ts +335 -0
- package/src/models/AiSession.ts +160 -0
- package/src/models/kinds.ts +2 -0
- package/src/sdk/AiSdk.ts +395 -0
- package/src/sdk/RouteBuilder.ts +27 -0
- package/src/sdk/Sdk.ts +3 -0
- package/src/sdk/SdkBase.ts +4 -0
- package/src/sdk/SdkMock.ts +185 -0
- package/tests/unit/mocking/current/Ai.spec.ts +109 -0
- package/tests/unit/modeling/ai/DataDomainDelta.spec.ts +419 -0
- package/tests/unit/modeling/ai/DomainAiTools.spec.ts +29 -0
- package/tests/unit/modeling/ai/DomainSerialization.spec.ts +143 -0
- package/tests/unit/modeling/ai/message_parser.spec.ts +157 -0
- package/tests/unit/modeling/ai/tools/DataDomain.tools.spec.ts +64 -0
- package/tests/unit/modeling/ai/tools/Semantic.tools.spec.ts +55 -0
- package/tests/unit/models/AiMessage.spec.ts +216 -0
- package/tests/unit/models/AiSession.spec.ts +147 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import { DataDomain } from '../../../../src/index.js'
|
|
3
|
+
import { SemanticType } from '../../../../src/modeling/Semantics.js'
|
|
4
|
+
import { DataDomainDelta } from '../../../../src/modeling/ai/DataDomainDelta.js'
|
|
5
|
+
import type { AiDomainDelta } from '../../../../src/modeling/ai/types.js'
|
|
6
|
+
|
|
7
|
+
test.group('DataDomainDelta.apply()', () => {
|
|
8
|
+
test('adds new models', ({ assert }) => {
|
|
9
|
+
const domain = new DataDomain()
|
|
10
|
+
const delta: AiDomainDelta = {
|
|
11
|
+
addedModels: [{ key: 'new-model', name: 'New Model', description: 'Testing new model' }],
|
|
12
|
+
}
|
|
13
|
+
const instance = new DataDomainDelta(domain)
|
|
14
|
+
instance.apply(delta)
|
|
15
|
+
const model = [...domain.listModels()][0]
|
|
16
|
+
assert.isDefined(model)
|
|
17
|
+
assert.equal(model!.info.name, 'New Model')
|
|
18
|
+
assert.equal(model!.info.description, 'Testing new model')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('does not duplicate existing models being added', ({ assert }) => {
|
|
22
|
+
const domain = new DataDomain()
|
|
23
|
+
domain.addModel({ key: 'existing-model', info: { name: 'Existing' } })
|
|
24
|
+
const delta: AiDomainDelta = {
|
|
25
|
+
addedModels: [{ key: 'existing-model', name: 'Should Not Override' }],
|
|
26
|
+
}
|
|
27
|
+
const instance = new DataDomainDelta(domain)
|
|
28
|
+
instance.apply(delta)
|
|
29
|
+
const model = domain.findModel('existing-model')
|
|
30
|
+
assert.equal(model!.info.name, 'Existing')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('modifies existing models', ({ assert }) => {
|
|
34
|
+
const domain = new DataDomain()
|
|
35
|
+
domain.addModel({ key: 'mod-model', info: { name: 'Old Name', description: 'Old desc' } })
|
|
36
|
+
const delta: AiDomainDelta = {
|
|
37
|
+
modifiedModels: [{ key: 'mod-model', name: 'New Name', description: 'New desc' }],
|
|
38
|
+
}
|
|
39
|
+
const instance = new DataDomainDelta(domain)
|
|
40
|
+
instance.apply(delta)
|
|
41
|
+
const model = domain.findModel('mod-model')
|
|
42
|
+
assert.equal(model!.info.name, 'New Name')
|
|
43
|
+
assert.equal(model!.info.description, 'New desc')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('deletes existing models', ({ assert }) => {
|
|
47
|
+
const domain = new DataDomain()
|
|
48
|
+
domain.addModel({ key: 'del-model' })
|
|
49
|
+
const delta: AiDomainDelta = { deletedModelKeys: ['del-model'] }
|
|
50
|
+
const instance = new DataDomainDelta(domain)
|
|
51
|
+
instance.apply(delta)
|
|
52
|
+
assert.isUndefined(domain.findModel('del-model'))
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('ignores deleting non-existing models', ({ assert }) => {
|
|
56
|
+
const domain = new DataDomain()
|
|
57
|
+
const delta: AiDomainDelta = { deletedModelKeys: ['fake-model'] }
|
|
58
|
+
const instance = new DataDomainDelta(domain)
|
|
59
|
+
assert.doesNotThrow(() => instance.apply(delta))
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('adds new entities', ({ assert }) => {
|
|
63
|
+
const domain = new DataDomain()
|
|
64
|
+
const delta: AiDomainDelta = {
|
|
65
|
+
addedEntities: [
|
|
66
|
+
{
|
|
67
|
+
key: 'new-entity',
|
|
68
|
+
modelKey: 'test-model',
|
|
69
|
+
name: 'New Entity',
|
|
70
|
+
properties: [{ key: 'new-prop', type: 'string', name: 'New Prop' }],
|
|
71
|
+
associations: [{ key: 'new-assoc', name: 'New Assoc', targets: [] }],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
const instance = new DataDomainDelta(domain)
|
|
76
|
+
instance.apply(delta)
|
|
77
|
+
|
|
78
|
+
// Check auto-created model
|
|
79
|
+
const createdModel =
|
|
80
|
+
domain.findModel('test-model') || Array.from(domain.listModels()).find((m) => m.info.name === 'test-model')
|
|
81
|
+
assert.isDefined(createdModel)
|
|
82
|
+
|
|
83
|
+
const entity = [...createdModel!.listEntities()][0]
|
|
84
|
+
assert.isDefined(entity)
|
|
85
|
+
assert.equal(entity!.info.name, 'New Entity')
|
|
86
|
+
assert.lengthOf(Array.from(entity!.listProperties()), 1)
|
|
87
|
+
assert.lengthOf(Array.from(entity!.listAssociations()), 1)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('deletes existing entities', ({ assert }) => {
|
|
91
|
+
const domain = new DataDomain()
|
|
92
|
+
const model = domain.addModel()
|
|
93
|
+
domain.addEntity(model.key, { key: 'del-entity' })
|
|
94
|
+
const delta: AiDomainDelta = { deletedEntityKeys: ['del-entity'] }
|
|
95
|
+
const instance = new DataDomainDelta(domain)
|
|
96
|
+
instance.apply(delta)
|
|
97
|
+
assert.isUndefined(domain.findEntity('del-entity'))
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('modifies existing entities', ({ assert }) => {
|
|
101
|
+
const domain = new DataDomain()
|
|
102
|
+
const model = domain.addModel({ key: 'mod-model' })
|
|
103
|
+
domain.addEntity(model.key, { key: 'mod-entity', info: { name: 'Old' } })
|
|
104
|
+
|
|
105
|
+
const delta: AiDomainDelta = {
|
|
106
|
+
modifiedEntities: [
|
|
107
|
+
{
|
|
108
|
+
key: 'mod-entity',
|
|
109
|
+
name: 'New Entity Name',
|
|
110
|
+
displayName: 'New Display Name',
|
|
111
|
+
description: 'New Description',
|
|
112
|
+
addedSemantics: [{ id: SemanticType.User }],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
}
|
|
116
|
+
const instance = new DataDomainDelta(domain)
|
|
117
|
+
instance.apply(delta)
|
|
118
|
+
|
|
119
|
+
const modifiedEntity = domain.findEntity('mod-entity')
|
|
120
|
+
assert.isDefined(modifiedEntity)
|
|
121
|
+
assert.equal(modifiedEntity!.info.name, 'New Entity Name')
|
|
122
|
+
assert.equal(modifiedEntity!.info.displayName, 'New Display Name')
|
|
123
|
+
assert.equal(modifiedEntity!.info.description, 'New Description')
|
|
124
|
+
|
|
125
|
+
const semantics = modifiedEntity!.semantics
|
|
126
|
+
assert.isDefined(semantics)
|
|
127
|
+
assert.lengthOf(semantics!, 1)
|
|
128
|
+
assert.equal(semantics![0].id, SemanticType.User)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('re-attaches entity to a new model', ({ assert }) => {
|
|
132
|
+
const domain = new DataDomain()
|
|
133
|
+
const model1 = domain.addModel({ key: 'model1' })
|
|
134
|
+
const model2 = domain.addModel({ key: 'model2' })
|
|
135
|
+
domain.addEntity(model1.key, { key: 'move-entity' })
|
|
136
|
+
|
|
137
|
+
const delta: AiDomainDelta = {
|
|
138
|
+
modifiedEntities: [
|
|
139
|
+
{
|
|
140
|
+
key: 'move-entity',
|
|
141
|
+
modelKey: 'model2',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
}
|
|
145
|
+
const instance = new DataDomainDelta(domain)
|
|
146
|
+
instance.apply(delta)
|
|
147
|
+
|
|
148
|
+
const movedEntity = domain.findEntity('move-entity')
|
|
149
|
+
assert.isDefined(movedEntity)
|
|
150
|
+
assert.equal(movedEntity!.getParentInstance()?.key, model2.key)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('modifies entity properties', ({ assert }) => {
|
|
154
|
+
const domain = new DataDomain()
|
|
155
|
+
const model = domain.addModel()
|
|
156
|
+
const entity = domain.addEntity(model.key, { info: { name: 'prop-entity' } })
|
|
157
|
+
entity.addProperty({ key: 'prop1', type: 'string', info: { name: 'Old' } })
|
|
158
|
+
|
|
159
|
+
const delta: AiDomainDelta = {
|
|
160
|
+
modifiedEntities: [
|
|
161
|
+
{
|
|
162
|
+
key: entity.key,
|
|
163
|
+
addedProperties: [{ key: 'prop2', type: 'number', name: 'New Prop' }],
|
|
164
|
+
modifiedProperties: [
|
|
165
|
+
{
|
|
166
|
+
key: 'prop1',
|
|
167
|
+
name: 'Modified Prop',
|
|
168
|
+
type: 'boolean',
|
|
169
|
+
constraints: { required: true },
|
|
170
|
+
schema: { minimum: 5 },
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
const instance = new DataDomainDelta(domain)
|
|
177
|
+
instance.apply(delta)
|
|
178
|
+
|
|
179
|
+
const [prop1, prop2] = [...entity.listProperties()]
|
|
180
|
+
|
|
181
|
+
assert.isDefined(prop1)
|
|
182
|
+
assert.equal(prop1!.info.name, 'Modified Prop')
|
|
183
|
+
assert.equal(prop1!.type, 'boolean')
|
|
184
|
+
assert.isTrue(prop1!.required)
|
|
185
|
+
assert.equal(prop1!.schema?.minimum, 5)
|
|
186
|
+
|
|
187
|
+
assert.isDefined(prop2)
|
|
188
|
+
assert.equal(prop2!.type, 'number')
|
|
189
|
+
assert.equal(prop2!.info.name, 'New Prop')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('deletes entity properties', ({ assert }) => {
|
|
193
|
+
const domain = new DataDomain()
|
|
194
|
+
const model = domain.addModel()
|
|
195
|
+
const entity = domain.addEntity(model.key, { key: 'prop-del-entity' })
|
|
196
|
+
entity.addProperty({ key: 'prop1', type: 'string' })
|
|
197
|
+
|
|
198
|
+
const delta: AiDomainDelta = {
|
|
199
|
+
modifiedEntities: [
|
|
200
|
+
{
|
|
201
|
+
key: 'prop-del-entity',
|
|
202
|
+
deletedPropertyKeys: ['prop1'],
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
const instance = new DataDomainDelta(domain)
|
|
207
|
+
instance.apply(delta)
|
|
208
|
+
assert.isUndefined(domain.findProperty('prop1'))
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('modifies entity associations', ({ assert }) => {
|
|
212
|
+
const domain = new DataDomain()
|
|
213
|
+
const model = domain.addModel()
|
|
214
|
+
const entity = domain.addEntity(model.key, { info: { name: 'assoc-entity' } })
|
|
215
|
+
const targetEntity1 = domain.addEntity(model.key, { key: 'target1', info: { name: 'Target 1' } })
|
|
216
|
+
const targetEntity2 = domain.addEntity(model.key, { key: 'target2', info: { name: 'Target 2' } })
|
|
217
|
+
entity.addAssociation({ key: 'assoc1', info: { name: 'Old Assoc' } })
|
|
218
|
+
|
|
219
|
+
const delta: AiDomainDelta = {
|
|
220
|
+
modifiedEntities: [
|
|
221
|
+
{
|
|
222
|
+
key: entity.key,
|
|
223
|
+
addedAssociations: [{ key: 'assoc2', name: 'New Assoc', targets: [{ key: targetEntity2.key }] }],
|
|
224
|
+
modifiedAssociations: [
|
|
225
|
+
{ key: 'assoc1', name: 'Modified Assoc', required: true, targets: [{ key: targetEntity1.key }] },
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
}
|
|
230
|
+
const instance = new DataDomainDelta(domain)
|
|
231
|
+
instance.apply(delta)
|
|
232
|
+
|
|
233
|
+
const [assoc1, assoc2] = [...entity.listAssociations()]
|
|
234
|
+
|
|
235
|
+
assert.isDefined(assoc1)
|
|
236
|
+
assert.equal(assoc1!.info.name, 'Modified Assoc')
|
|
237
|
+
assert.isTrue(assoc1!.required)
|
|
238
|
+
assert.includeMembers(
|
|
239
|
+
assoc1!.targets.map((t) => t.key),
|
|
240
|
+
[targetEntity1.key]
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
assert.isDefined(assoc2)
|
|
244
|
+
assert.equal(assoc2!.info.name, 'New Assoc')
|
|
245
|
+
assert.includeMembers(
|
|
246
|
+
assoc2!.targets.map((t) => t.key),
|
|
247
|
+
[targetEntity2.key]
|
|
248
|
+
)
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
test('deletes entity associations', ({ assert }) => {
|
|
252
|
+
const domain = new DataDomain()
|
|
253
|
+
const model = domain.addModel()
|
|
254
|
+
const entity = domain.addEntity(model.key, { key: 'assoc-del-entity' })
|
|
255
|
+
entity.addAssociation({ key: 'assoc1' })
|
|
256
|
+
|
|
257
|
+
const delta: AiDomainDelta = {
|
|
258
|
+
modifiedEntities: [
|
|
259
|
+
{
|
|
260
|
+
key: 'assoc-del-entity',
|
|
261
|
+
deletedAssociationKeys: ['assoc1'],
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
const instance = new DataDomainDelta(domain)
|
|
266
|
+
instance.apply(delta)
|
|
267
|
+
assert.isUndefined(domain.findAssociation('assoc1'))
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
test('adds, modifies, and deletes semantics on an entity', ({ assert }) => {
|
|
271
|
+
const domain = new DataDomain()
|
|
272
|
+
const model = domain.addModel()
|
|
273
|
+
const entity = domain.addEntity(model.key, { key: 'semantic-entity' })
|
|
274
|
+
entity.addSemantic({ id: SemanticType.Address })
|
|
275
|
+
|
|
276
|
+
const delta: AiDomainDelta = {
|
|
277
|
+
modifiedEntities: [
|
|
278
|
+
{
|
|
279
|
+
key: 'semantic-entity',
|
|
280
|
+
addedSemantics: [{ id: SemanticType.User }],
|
|
281
|
+
modifiedSemantics: [{ id: SemanticType.Address, config: { someVal: true } }],
|
|
282
|
+
deletedSemanticIds: [SemanticType.Tags],
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
}
|
|
286
|
+
const instance = new DataDomainDelta(domain)
|
|
287
|
+
instance.apply(delta)
|
|
288
|
+
|
|
289
|
+
const semantics = entity.semantics
|
|
290
|
+
assert.isDefined(semantics)
|
|
291
|
+
assert.lengthOf(semantics!, 2)
|
|
292
|
+
const intSem = semantics!.find((s) => s.id === SemanticType.Address)
|
|
293
|
+
assert.isDefined(intSem)
|
|
294
|
+
assert.deepEqual(intSem!.config, { someVal: true })
|
|
295
|
+
|
|
296
|
+
const userSem = semantics!.find((s) => s.id === SemanticType.User)
|
|
297
|
+
assert.isDefined(userSem)
|
|
298
|
+
|
|
299
|
+
// Now delete
|
|
300
|
+
const deltaDel: AiDomainDelta = {
|
|
301
|
+
modifiedEntities: [
|
|
302
|
+
{
|
|
303
|
+
key: 'semantic-entity',
|
|
304
|
+
deletedSemanticIds: [SemanticType.User],
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
}
|
|
308
|
+
instance.apply(deltaDel)
|
|
309
|
+
assert.lengthOf(entity.semantics!, 1)
|
|
310
|
+
assert.isUndefined(entity.semantics!.find((s) => s.id === SemanticType.User))
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test('adds semantics to properties and associations', ({ assert }) => {
|
|
314
|
+
const domain = new DataDomain()
|
|
315
|
+
const model = domain.addModel()
|
|
316
|
+
const entity = domain.addEntity(model.key, { key: 'sem-prop-entity' })
|
|
317
|
+
entity.addProperty({ key: 'prop1', type: 'string' })
|
|
318
|
+
entity.addAssociation({ key: 'assoc1' })
|
|
319
|
+
|
|
320
|
+
const delta: AiDomainDelta = {
|
|
321
|
+
modifiedEntities: [
|
|
322
|
+
{
|
|
323
|
+
key: entity.key,
|
|
324
|
+
modifiedProperties: [
|
|
325
|
+
{
|
|
326
|
+
key: 'prop1',
|
|
327
|
+
addedSemantics: [{ id: SemanticType.Password }],
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
modifiedAssociations: [
|
|
331
|
+
{
|
|
332
|
+
key: 'assoc1',
|
|
333
|
+
addedSemantics: [{ id: SemanticType.Tags }], // Example valid for association in some contexts
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
}
|
|
339
|
+
const instance = new DataDomainDelta(domain)
|
|
340
|
+
instance.apply(delta)
|
|
341
|
+
|
|
342
|
+
const prop1 = domain.findProperty('prop1')
|
|
343
|
+
assert.lengthOf(prop1!.semantics!, 1)
|
|
344
|
+
assert.equal(prop1!.semantics![0].id, SemanticType.Password)
|
|
345
|
+
|
|
346
|
+
const assoc1 = domain.findAssociation('assoc1')
|
|
347
|
+
assert.lengthOf(assoc1!.semantics!, 1)
|
|
348
|
+
assert.equal(assoc1!.semantics![0].id, SemanticType.Tags)
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
test.group('DataDomainDelta.normalize()', () => {
|
|
353
|
+
test('merges duplicated entities', ({ assert }) => {
|
|
354
|
+
const delta: AiDomainDelta = {
|
|
355
|
+
modifiedEntities: [
|
|
356
|
+
{ key: 'ent1', name: 'First Name', addedProperties: [{ key: 'p1', type: 'string', name: 'Prop 1' }] },
|
|
357
|
+
{ key: 'ent1', displayName: 'Display', addedProperties: [{ key: 'p2', type: 'number', name: 'Prop 2' }] },
|
|
358
|
+
],
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const normalized = DataDomainDelta.normalize(delta)
|
|
362
|
+
assert.lengthOf(normalized.modifiedEntities!, 1)
|
|
363
|
+
|
|
364
|
+
const ent = normalized.modifiedEntities![0]
|
|
365
|
+
assert.equal(ent.name, 'First Name')
|
|
366
|
+
assert.equal(ent.displayName, 'Display')
|
|
367
|
+
assert.lengthOf(ent.addedProperties!, 2)
|
|
368
|
+
assert.equal(ent.addedProperties![0].key, 'p1')
|
|
369
|
+
assert.equal(ent.addedProperties![1].key, 'p2')
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
test('merges primitive arrays like tags and deleted keys', ({ assert }) => {
|
|
373
|
+
const delta: AiDomainDelta = {
|
|
374
|
+
modifiedEntities: [
|
|
375
|
+
{ key: 'ent1', tags: ['a'], deletedPropertyKeys: ['p1'] },
|
|
376
|
+
{ key: 'ent1', tags: ['b', 'a'], deletedPropertyKeys: ['p2', 'p1'] },
|
|
377
|
+
],
|
|
378
|
+
deletedModelKeys: ['m1', 'm2', 'm1'],
|
|
379
|
+
deletedEntityKeys: ['e1', 'e1'],
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const normalized = DataDomainDelta.normalize(delta)
|
|
383
|
+
const ent = normalized.modifiedEntities![0]
|
|
384
|
+
assert.sameMembers(ent.tags!, ['a', 'b'])
|
|
385
|
+
assert.sameMembers(ent.deletedPropertyKeys!, ['p1', 'p2'])
|
|
386
|
+
|
|
387
|
+
assert.sameMembers(normalized.deletedModelKeys!, ['m1', 'm2'])
|
|
388
|
+
assert.sameMembers(normalized.deletedEntityKeys!, ['e1'])
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
test('merges nested properties and semantics', ({ assert }) => {
|
|
392
|
+
const delta: AiDomainDelta = {
|
|
393
|
+
modifiedEntities: [
|
|
394
|
+
{
|
|
395
|
+
key: 'ent1',
|
|
396
|
+
modifiedProperties: [{ key: 'p1', name: 'Name', constraints: { required: true } }],
|
|
397
|
+
addedSemantics: [{ id: SemanticType.User, config: { a: 1 } }],
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
key: 'ent1',
|
|
401
|
+
modifiedProperties: [{ key: 'p1', type: 'number', constraints: { unique: true } }],
|
|
402
|
+
addedSemantics: [{ id: SemanticType.User, config: { b: 2 } }],
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const normalized = DataDomainDelta.normalize(delta)
|
|
408
|
+
const ent = normalized.modifiedEntities![0]
|
|
409
|
+
|
|
410
|
+
assert.lengthOf(ent.modifiedProperties!, 1)
|
|
411
|
+
const p1 = ent.modifiedProperties![0]
|
|
412
|
+
assert.equal(p1.name, 'Name')
|
|
413
|
+
assert.equal(p1.type, 'number')
|
|
414
|
+
assert.deepEqual(p1.constraints, { required: true, unique: true })
|
|
415
|
+
|
|
416
|
+
assert.lengthOf(ent.addedSemantics!, 1)
|
|
417
|
+
assert.deepEqual(ent.addedSemantics![0].config, { a: 1, b: 2 })
|
|
418
|
+
})
|
|
419
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import { tools, functions } from '../../../../src/modeling/ai/domain_tools.js'
|
|
3
|
+
|
|
4
|
+
test.group('DomainAiTools', () => {
|
|
5
|
+
test('all declared functions have an implementation', ({ assert }) => {
|
|
6
|
+
const declarations = tools[0].functionDeclarations!
|
|
7
|
+
for (const decl of declarations) {
|
|
8
|
+
assert.property(
|
|
9
|
+
functions,
|
|
10
|
+
decl.name as string,
|
|
11
|
+
`Function declaration ${decl.name} does not have a corresponding implementation in functions object`
|
|
12
|
+
)
|
|
13
|
+
assert.isFunction(functions[decl.name as string], `Implementation for ${decl.name} must be a function`)
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('all implemented functions have a declaration', ({ assert }) => {
|
|
18
|
+
const declarations = tools[0].functionDeclarations!
|
|
19
|
+
const declaredNames = declarations.map((d) => d.name)
|
|
20
|
+
|
|
21
|
+
for (const funcName of Object.keys(functions)) {
|
|
22
|
+
assert.include(
|
|
23
|
+
declaredNames,
|
|
24
|
+
funcName,
|
|
25
|
+
`Implemented function ${funcName} does not have a corresponding declaration in tools object`
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import { serializeDomainForAi, serializeEntity } from '../../../../src/modeling/ai/DomainSerialization.js'
|
|
3
|
+
import { DataDomain } from '../../../../src/index.js'
|
|
4
|
+
import { SemanticType } from '../../../../src/modeling/Semantics.js'
|
|
5
|
+
|
|
6
|
+
test.group('DomainSerialization', () => {
|
|
7
|
+
test('serializeDomainForAi correctly serializes models, entities, and namespaces', ({ assert }) => {
|
|
8
|
+
const domain = new DataDomain({
|
|
9
|
+
info: { name: 'Test Domain', description: 'Testing out serialization' },
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const rootModel = domain.addModel({
|
|
13
|
+
info: { name: 'Root Model' },
|
|
14
|
+
})
|
|
15
|
+
const rootEntity = domain.addEntity(rootModel.key, {
|
|
16
|
+
info: { name: 'Root Entity' },
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
// Add a namespace
|
|
20
|
+
const namespace = domain.addNamespace({
|
|
21
|
+
info: { name: 'Core Namespace', description: 'Core items' },
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const nsModel = domain.addModel({ info: { name: 'NS Model' } }, namespace.key)
|
|
25
|
+
const nsEntity = domain.addEntity(nsModel.key, { info: { name: 'NS Entity' } })
|
|
26
|
+
|
|
27
|
+
// Perform serialization
|
|
28
|
+
const serializedDomain = serializeDomainForAi(domain)
|
|
29
|
+
|
|
30
|
+
// Domain Assertions
|
|
31
|
+
assert.equal(serializedDomain.key, domain.key)
|
|
32
|
+
assert.equal(serializedDomain.name, 'Test Domain')
|
|
33
|
+
|
|
34
|
+
// Models Assertions (Should include both root and namespace models due to processNamespace recursion)
|
|
35
|
+
assert.lengthOf(serializedDomain.models, 2)
|
|
36
|
+
const serializedRootModel = serializedDomain.models.find((m) => m.key === rootModel.key)
|
|
37
|
+
assert.isDefined(serializedRootModel)
|
|
38
|
+
assert.equal(serializedRootModel!.name, 'Root Model')
|
|
39
|
+
|
|
40
|
+
const serializedNsModel = serializedDomain.models.find((m) => m.key === nsModel.key)
|
|
41
|
+
assert.isDefined(serializedNsModel)
|
|
42
|
+
assert.equal(serializedNsModel!.name, 'NS Model')
|
|
43
|
+
|
|
44
|
+
// Entities Assertions
|
|
45
|
+
assert.lengthOf(serializedDomain.entities, 2)
|
|
46
|
+
const serializedRootEntity = serializedDomain.entities.find((e) => e.key === rootEntity.key)
|
|
47
|
+
assert.isDefined(serializedRootEntity)
|
|
48
|
+
assert.equal(serializedRootEntity!.modelKey, rootModel.key)
|
|
49
|
+
assert.equal(serializedRootEntity!.name, 'Root Entity')
|
|
50
|
+
|
|
51
|
+
const serializedNsEntity = serializedDomain.entities.find((e) => e.key === nsEntity.key)
|
|
52
|
+
assert.isDefined(serializedNsEntity)
|
|
53
|
+
assert.equal(serializedNsEntity!.modelKey, nsModel.key)
|
|
54
|
+
assert.equal(serializedNsEntity!.name, 'NS Entity')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('serializeEntity correctly serializes properties, associations, and semantics', ({ assert }) => {
|
|
58
|
+
const domain = new DataDomain()
|
|
59
|
+
const model = domain.addModel({ info: { name: 'User Model' } })
|
|
60
|
+
const entity = domain.addEntity(model.key, {
|
|
61
|
+
info: { name: 'User', displayName: 'System User', description: 'User entity' },
|
|
62
|
+
tags: ['auth', 'core'],
|
|
63
|
+
})
|
|
64
|
+
entity.addSemantic({ id: SemanticType.User })
|
|
65
|
+
|
|
66
|
+
const stringProp = entity.addProperty({
|
|
67
|
+
type: 'string',
|
|
68
|
+
info: { name: 'username', displayName: 'Username', description: 'The username' },
|
|
69
|
+
required: true,
|
|
70
|
+
unique: true,
|
|
71
|
+
schema: {
|
|
72
|
+
minimum: 3,
|
|
73
|
+
maximum: 50,
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
stringProp.addSemantic({ id: SemanticType.Username })
|
|
77
|
+
|
|
78
|
+
const booleanProp = entity.addProperty({
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
info: { name: 'isActive', displayName: 'Active', description: 'Is the user active' },
|
|
81
|
+
})
|
|
82
|
+
booleanProp.addSemantic({ id: SemanticType.Calculated, config: { formula: 'true' } })
|
|
83
|
+
|
|
84
|
+
const otherModel = domain.addModel()
|
|
85
|
+
const otherEntity = domain.addEntity(otherModel.key, { info: { name: 'Role' } })
|
|
86
|
+
|
|
87
|
+
const assoc = entity.addAssociation({
|
|
88
|
+
info: { name: 'roles', displayName: 'Roles', description: 'User roles' },
|
|
89
|
+
multiple: true,
|
|
90
|
+
required: false,
|
|
91
|
+
})
|
|
92
|
+
assoc.addTarget(otherEntity.key)
|
|
93
|
+
assoc.addSemantic({ id: SemanticType.Tags })
|
|
94
|
+
|
|
95
|
+
// Perform Entity Full Serialization Assertions
|
|
96
|
+
const serializedEntity = serializeEntity(entity, model.key)
|
|
97
|
+
assert.equal(serializedEntity.key, entity.key)
|
|
98
|
+
assert.equal(serializedEntity.modelKey, model.key)
|
|
99
|
+
assert.equal(serializedEntity.name, 'User')
|
|
100
|
+
assert.equal(serializedEntity.displayName, 'System User')
|
|
101
|
+
assert.equal(serializedEntity.description, 'User entity')
|
|
102
|
+
assert.deepEqual(serializedEntity.tags, ['auth', 'core'])
|
|
103
|
+
|
|
104
|
+
// Entity Semantics
|
|
105
|
+
assert.isDefined(serializedEntity.semantics)
|
|
106
|
+
assert.lengthOf(serializedEntity.semantics!, 1)
|
|
107
|
+
assert.equal(serializedEntity.semantics![0].id, SemanticType.User)
|
|
108
|
+
|
|
109
|
+
// Properties Assertions
|
|
110
|
+
assert.isDefined(serializedEntity.properties)
|
|
111
|
+
assert.lengthOf(serializedEntity.properties!, 2)
|
|
112
|
+
|
|
113
|
+
const serializedStringProp = serializedEntity.properties!.find((p) => p.name === 'username')
|
|
114
|
+
assert.isDefined(serializedStringProp)
|
|
115
|
+
assert.equal(serializedStringProp!.type, 'string')
|
|
116
|
+
assert.equal(serializedStringProp!.displayName, 'Username')
|
|
117
|
+
assert.equal(serializedStringProp!.description, 'The username')
|
|
118
|
+
assert.deepEqual(serializedStringProp!.constraints, { required: true, unique: true })
|
|
119
|
+
assert.deepEqual(serializedStringProp!.schema, { minimum: 3, maximum: 50 })
|
|
120
|
+
assert.isDefined(serializedStringProp!.semantics)
|
|
121
|
+
assert.equal(serializedStringProp!.semantics![0].id, SemanticType.Username)
|
|
122
|
+
|
|
123
|
+
const serializedBoolProp = serializedEntity.properties!.find((p) => p.name === 'isActive')
|
|
124
|
+
assert.isDefined(serializedBoolProp)
|
|
125
|
+
assert.equal(serializedBoolProp!.type, 'boolean')
|
|
126
|
+
assert.isDefined(serializedBoolProp!.semantics)
|
|
127
|
+
assert.equal(serializedBoolProp!.semantics![0].id, SemanticType.Calculated)
|
|
128
|
+
assert.deepEqual(serializedBoolProp!.semantics![0].config, { formula: 'true' })
|
|
129
|
+
|
|
130
|
+
// Association Assertions
|
|
131
|
+
assert.isDefined(serializedEntity.associations)
|
|
132
|
+
assert.lengthOf(serializedEntity.associations!, 1)
|
|
133
|
+
|
|
134
|
+
const serializedAssoc = serializedEntity.associations![0]
|
|
135
|
+
assert.equal(serializedAssoc.key, assoc.key)
|
|
136
|
+
assert.equal(serializedAssoc.name, 'roles')
|
|
137
|
+
assert.equal(serializedAssoc.displayName, 'Roles')
|
|
138
|
+
assert.equal(serializedAssoc.description, 'User roles')
|
|
139
|
+
assert.equal(serializedAssoc.required, false)
|
|
140
|
+
assert.isDefined(serializedAssoc.semantics)
|
|
141
|
+
assert.equal(serializedAssoc.semantics![0].id, SemanticType.Tags)
|
|
142
|
+
})
|
|
143
|
+
})
|