@api-client/core 0.18.2 → 0.18.4
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/modeling/ApiModel.d.ts +5 -5
- package/build/src/modeling/ApiModel.d.ts.map +1 -1
- package/build/src/modeling/ApiModel.js +8 -8
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/helpers/database.d.ts +24 -0
- package/build/src/modeling/helpers/database.d.ts.map +1 -0
- package/build/src/modeling/helpers/database.js +42 -0
- package/build/src/modeling/helpers/database.js.map +1 -0
- package/build/src/modeling/importers/CsvImporter.d.ts +36 -0
- package/build/src/modeling/importers/CsvImporter.d.ts.map +1 -0
- package/build/src/modeling/importers/CsvImporter.js +81 -0
- package/build/src/modeling/importers/CsvImporter.js.map +1 -0
- package/build/src/modeling/importers/ImporterException.d.ts +10 -0
- package/build/src/modeling/importers/ImporterException.d.ts.map +1 -0
- package/build/src/modeling/importers/ImporterException.js +10 -0
- package/build/src/modeling/importers/ImporterException.js.map +1 -0
- package/build/src/modeling/importers/JsonSchemaImporter.d.ts +99 -0
- package/build/src/modeling/importers/JsonSchemaImporter.d.ts.map +1 -0
- package/build/src/modeling/importers/JsonSchemaImporter.js +525 -0
- package/build/src/modeling/importers/JsonSchemaImporter.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +13 -13
- package/package.json +5 -2
- package/src/modeling/ApiModel.ts +11 -10
- package/src/modeling/helpers/database.ts +48 -0
- package/src/modeling/importers/CsvImporter.ts +88 -0
- package/src/modeling/importers/ImporterException.ts +10 -0
- package/src/modeling/importers/JsonSchemaImporter.ts +642 -0
- package/tests/unit/modeling/api_model.spec.ts +9 -9
- package/tests/unit/modeling/importers/csv_importer.spec.ts +189 -0
- package/tests/unit/modeling/importers/json_schema_importer.spec.ts +451 -0
|
@@ -21,7 +21,7 @@ test.group('ApiModel.createSchema()', (g) => {
|
|
|
21
21
|
assert.isNotEmpty(schema.key)
|
|
22
22
|
assert.deepInclude(schema.info, { name: 'Unnamed API' })
|
|
23
23
|
assert.deepEqual(schema.exposes, [])
|
|
24
|
-
assert.isUndefined(schema.
|
|
24
|
+
assert.isUndefined(schema.user)
|
|
25
25
|
assert.isUndefined(schema.dependencyList)
|
|
26
26
|
assert.isUndefined(schema.authentication)
|
|
27
27
|
assert.isUndefined(schema.authorization)
|
|
@@ -38,7 +38,7 @@ test.group('ApiModel.createSchema()', (g) => {
|
|
|
38
38
|
key: 'test-api',
|
|
39
39
|
info: { name: 'Test API', description: 'A test API' },
|
|
40
40
|
exposes: [{ key: 'entity1', actions: [] }],
|
|
41
|
-
|
|
41
|
+
user: { key: 'user-entity' },
|
|
42
42
|
dependencyList: [{ key: 'domain1', version: '1.0.0' }],
|
|
43
43
|
authentication: { strategy: 'UsernamePassword' },
|
|
44
44
|
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
@@ -55,7 +55,7 @@ test.group('ApiModel.createSchema()', (g) => {
|
|
|
55
55
|
assert.equal(schema.key, 'test-api')
|
|
56
56
|
assert.deepInclude(schema.info, { name: 'Test API', description: 'A test API' })
|
|
57
57
|
assert.deepEqual(schema.exposes, [{ key: 'entity1', actions: [] }])
|
|
58
|
-
assert.
|
|
58
|
+
assert.deepEqual(schema.user, { key: 'user-entity' })
|
|
59
59
|
assert.deepEqual(schema.dependencyList, [{ key: 'domain1', version: '1.0.0' }])
|
|
60
60
|
assert.deepEqual(schema.authentication, { strategy: 'UsernamePassword' })
|
|
61
61
|
assert.deepEqual(schema.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
@@ -90,7 +90,7 @@ test.group('ApiModel.constructor()', (g) => {
|
|
|
90
90
|
assert.isNotEmpty(model.key)
|
|
91
91
|
assert.equal(model.info.name, 'Unnamed API')
|
|
92
92
|
assert.deepEqual(model.exposes, [])
|
|
93
|
-
assert.isUndefined(model.
|
|
93
|
+
assert.isUndefined(model.user)
|
|
94
94
|
assert.isUndefined(model.authentication)
|
|
95
95
|
assert.isUndefined(model.authorization)
|
|
96
96
|
assert.isUndefined(model.session)
|
|
@@ -108,7 +108,7 @@ test.group('ApiModel.constructor()', (g) => {
|
|
|
108
108
|
key: 'test-api',
|
|
109
109
|
info: { name: 'Test API', description: 'A test API' },
|
|
110
110
|
exposes: [{ key: 'entity1', actions: [] }],
|
|
111
|
-
|
|
111
|
+
user: { key: 'user-entity' },
|
|
112
112
|
dependencyList: [{ key: 'domain1', version: '1.0.0' }],
|
|
113
113
|
authentication: { strategy: 'UsernamePassword' },
|
|
114
114
|
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
@@ -124,7 +124,7 @@ test.group('ApiModel.constructor()', (g) => {
|
|
|
124
124
|
assert.equal(model.key, 'test-api')
|
|
125
125
|
assert.equal(model.info.name, 'Test API')
|
|
126
126
|
assert.deepEqual(model.exposes, [{ key: 'entity1', actions: [] }])
|
|
127
|
-
assert.
|
|
127
|
+
assert.deepEqual(model.user, { key: 'user-entity' })
|
|
128
128
|
assert.deepEqual(model.dependencyList, [{ key: 'domain1', version: '1.0.0' }])
|
|
129
129
|
assert.deepEqual(model.authentication, { strategy: 'UsernamePassword' })
|
|
130
130
|
assert.deepEqual(model.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
@@ -172,7 +172,7 @@ test.group('ApiModel.toJSON()', (g) => {
|
|
|
172
172
|
assert.equal(json.key, model.key)
|
|
173
173
|
assert.deepInclude(json.info, { name: 'Unnamed API' })
|
|
174
174
|
assert.deepEqual(json.exposes, [])
|
|
175
|
-
assert.isUndefined(json.
|
|
175
|
+
assert.isUndefined(json.user)
|
|
176
176
|
assert.isUndefined(json.dependencyList)
|
|
177
177
|
assert.isUndefined(json.authentication)
|
|
178
178
|
assert.isUndefined(json.authorization)
|
|
@@ -190,7 +190,7 @@ test.group('ApiModel.toJSON()', (g) => {
|
|
|
190
190
|
key: 'test-api',
|
|
191
191
|
info: { name: 'Test API', description: 'A test API' },
|
|
192
192
|
exposes: [{ key: 'entity1', actions: [] }],
|
|
193
|
-
|
|
193
|
+
user: { key: 'user-entity' },
|
|
194
194
|
dependencyList: [{ key: 'domain1', version: '1.0.0' }],
|
|
195
195
|
authentication: { strategy: 'UsernamePassword' },
|
|
196
196
|
authorization: { strategy: 'RBAC', roleKey: 'role' } as RolesBasedAccessControl,
|
|
@@ -207,7 +207,7 @@ test.group('ApiModel.toJSON()', (g) => {
|
|
|
207
207
|
assert.equal(json.key, 'test-api')
|
|
208
208
|
assert.deepInclude(json.info, { name: 'Test API', description: 'A test API' })
|
|
209
209
|
assert.deepEqual(json.exposes, [{ key: 'entity1', actions: [] }])
|
|
210
|
-
assert.
|
|
210
|
+
assert.deepEqual(json.user, { key: 'user-entity' })
|
|
211
211
|
assert.deepEqual(json.dependencyList, [{ key: 'domain1', version: '1.0.0' }])
|
|
212
212
|
assert.deepEqual(json.authentication, { strategy: 'UsernamePassword' })
|
|
213
213
|
assert.deepEqual(json.authorization, { strategy: 'RBAC', roleKey: 'role' })
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
|
|
3
|
+
import { DataDomain } from '../../../../src/modeling/DataDomain.js'
|
|
4
|
+
import { DomainModel } from '../../../../src/modeling/DomainModel.js'
|
|
5
|
+
import { DomainEntity } from '../../../../src/modeling/DomainEntity.js'
|
|
6
|
+
import { DomainProperty } from '../../../../src/modeling/DomainProperty.js'
|
|
7
|
+
import { CsvImporter, type ParseResult } from '../../../../src/modeling/importers/CsvImporter.js'
|
|
8
|
+
|
|
9
|
+
function findModelByName(domain: DataDomain, name: string): DomainModel {
|
|
10
|
+
for (const model of domain.listModels()) {
|
|
11
|
+
if (model.info.name === name || model.info.displayName === name) {
|
|
12
|
+
return model
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
throw new Error(`Model with name "${name}" not found`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function findEntityByName(domain: DataDomain, name: string): DomainEntity {
|
|
19
|
+
for (const entity of domain.listEntities()) {
|
|
20
|
+
if (entity.info.name === name || entity.info.displayName === name) {
|
|
21
|
+
return entity
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Entity with name "${name}" not found`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findPropertyByName(entity: DomainEntity, name: string): DomainProperty {
|
|
28
|
+
for (const prop of entity.listProperties()) {
|
|
29
|
+
if (prop.info.name === name || prop.info.displayName === name) {
|
|
30
|
+
return prop
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Property with name "${name}" not found`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test.group('CsvImporter: import', (group) => {
|
|
37
|
+
let domain: DataDomain
|
|
38
|
+
|
|
39
|
+
group.each.setup(() => {
|
|
40
|
+
domain = new DataDomain()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('should import a basic CSV structure', async ({ assert }) => {
|
|
44
|
+
const importer = new CsvImporter(domain)
|
|
45
|
+
const parseResult: ParseResult = {
|
|
46
|
+
format: [
|
|
47
|
+
{ index: 0, name: 'id', type: 'number', format: 'integer' },
|
|
48
|
+
{ index: 1, name: 'name', type: 'string' },
|
|
49
|
+
{ index: 2, name: 'value', type: 'number', format: 'decimal' },
|
|
50
|
+
],
|
|
51
|
+
values: [[1, 'test-item', 99.9]],
|
|
52
|
+
header: ['id', 'name', 'value'],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await importer.import(parseResult, 'My Products')
|
|
56
|
+
|
|
57
|
+
const model = findModelByName(domain, 'my_products')
|
|
58
|
+
assert.exists(model)
|
|
59
|
+
|
|
60
|
+
const entity = findEntityByName(domain, 'my_products')
|
|
61
|
+
assert.exists(entity)
|
|
62
|
+
assert.equal(entity!.info.displayName, 'My Products')
|
|
63
|
+
|
|
64
|
+
const idProp = findPropertyByName(entity!, 'id')
|
|
65
|
+
assert.exists(idProp)
|
|
66
|
+
assert.equal(idProp!.type, 'number')
|
|
67
|
+
assert.deepEqual(idProp!.bindings, [{ type: 'web', schema: { format: 'int64' } }])
|
|
68
|
+
assert.deepEqual(idProp!.schema?.examples, ['1'])
|
|
69
|
+
|
|
70
|
+
const nameProp = findPropertyByName(entity!, 'name')
|
|
71
|
+
assert.exists(nameProp)
|
|
72
|
+
assert.equal(nameProp!.type, 'string')
|
|
73
|
+
assert.isEmpty(nameProp!.bindings)
|
|
74
|
+
assert.deepEqual(nameProp!.schema?.examples, ['test-item'])
|
|
75
|
+
assert.isUndefined(nameProp!.info.displayName, 'should not have displayName set')
|
|
76
|
+
|
|
77
|
+
const valueProp = findPropertyByName(entity!, 'value')
|
|
78
|
+
assert.exists(valueProp)
|
|
79
|
+
assert.equal(valueProp!.type, 'number')
|
|
80
|
+
assert.deepEqual(valueProp!.bindings, [{ type: 'web', schema: { format: 'double' } }])
|
|
81
|
+
assert.deepEqual(valueProp!.schema?.examples, ['99.9'])
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('should handle name sanitization for model and properties', async ({ assert }) => {
|
|
85
|
+
const importer = new CsvImporter(domain)
|
|
86
|
+
const parseResult: ParseResult = {
|
|
87
|
+
format: [{ index: 0, name: 'First Name', type: 'string' }],
|
|
88
|
+
values: [['John']],
|
|
89
|
+
header: ['First Name'],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await importer.import(parseResult, 'User List!')
|
|
93
|
+
|
|
94
|
+
const model = findModelByName(domain, 'user_list')
|
|
95
|
+
assert.exists(model)
|
|
96
|
+
assert.equal(model!.info.displayName, 'User List!')
|
|
97
|
+
|
|
98
|
+
const entity = findEntityByName(domain, 'user_list')
|
|
99
|
+
assert.exists(entity)
|
|
100
|
+
assert.equal(entity!.info.displayName, 'User List!')
|
|
101
|
+
|
|
102
|
+
const prop = findPropertyByName(entity!, 'first_name')
|
|
103
|
+
assert.exists(prop)
|
|
104
|
+
assert.equal(prop!.info.displayName, 'First Name')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('should import with no data rows (headers only)', async ({ assert }) => {
|
|
108
|
+
const importer = new CsvImporter(domain)
|
|
109
|
+
const parseResult: ParseResult = {
|
|
110
|
+
format: [
|
|
111
|
+
{ index: 0, name: 'id', type: 'number' },
|
|
112
|
+
{ index: 1, name: 'email', type: 'string' },
|
|
113
|
+
],
|
|
114
|
+
values: [], // No data rows
|
|
115
|
+
header: ['id', 'email'],
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await importer.import(parseResult, 'users')
|
|
119
|
+
|
|
120
|
+
const entity = findEntityByName(domain, 'users')
|
|
121
|
+
assert.exists(entity)
|
|
122
|
+
|
|
123
|
+
const idProp = findPropertyByName(entity!, 'id')
|
|
124
|
+
assert.exists(idProp)
|
|
125
|
+
assert.isUndefined(idProp!.schema?.examples, 'should have no examples')
|
|
126
|
+
|
|
127
|
+
const emailProp = findPropertyByName(entity!, 'email')
|
|
128
|
+
assert.exists(emailProp)
|
|
129
|
+
assert.isUndefined(emailProp!.schema?.examples, 'should have no examples')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('should handle null and undefined values in example row', async ({ assert }) => {
|
|
133
|
+
const importer = new CsvImporter(domain)
|
|
134
|
+
const parseResult: ParseResult = {
|
|
135
|
+
format: [
|
|
136
|
+
{ index: 0, name: 'col_a', type: 'string' },
|
|
137
|
+
{ index: 1, name: 'col_b', type: 'string' },
|
|
138
|
+
{ index: 2, name: 'col_c', type: 'string' },
|
|
139
|
+
],
|
|
140
|
+
// @ts-expect-error TypeScript expects values to be defined
|
|
141
|
+
values: [[null, 'has-value', undefined]],
|
|
142
|
+
header: ['col_a', 'col_b', 'col_c'],
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await importer.import(parseResult, 'test_data')
|
|
146
|
+
|
|
147
|
+
const entity = findEntityByName(domain, 'test_data')
|
|
148
|
+
assert.exists(entity)
|
|
149
|
+
|
|
150
|
+
const propA = findPropertyByName(entity!, 'col_a')
|
|
151
|
+
assert.exists(propA)
|
|
152
|
+
assert.isUndefined(propA!.schema?.examples, 'should not create example for null')
|
|
153
|
+
|
|
154
|
+
const propB = findPropertyByName(entity!, 'col_b')
|
|
155
|
+
assert.exists(propB)
|
|
156
|
+
assert.deepEqual(propB!.schema?.examples, ['has-value'])
|
|
157
|
+
|
|
158
|
+
const propC = findPropertyByName(entity!, 'col_c')
|
|
159
|
+
assert.exists(propC)
|
|
160
|
+
assert.isUndefined(propC!.schema?.examples, 'should not create example for undefined')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('should handle integer and float number formats correctly', async ({ assert }) => {
|
|
164
|
+
const importer = new CsvImporter(domain)
|
|
165
|
+
const parseResult: ParseResult = {
|
|
166
|
+
format: [
|
|
167
|
+
{ index: 0, name: 'integer_val', type: 'number', format: 'integer' },
|
|
168
|
+
{ index: 1, name: 'float_val', type: 'number', format: 'decimal' },
|
|
169
|
+
],
|
|
170
|
+
values: [[123, 45.67]],
|
|
171
|
+
header: ['integer_val', 'float_val'],
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await importer.import(parseResult, 'Numeric Data')
|
|
175
|
+
|
|
176
|
+
const entity = findEntityByName(domain, 'numeric_data')
|
|
177
|
+
assert.exists(entity)
|
|
178
|
+
|
|
179
|
+
const intProp = findPropertyByName(entity!, 'integer_val')
|
|
180
|
+
assert.exists(intProp)
|
|
181
|
+
assert.equal(intProp!.type, 'number')
|
|
182
|
+
assert.deepEqual(intProp!.bindings, [{ type: 'web', schema: { format: 'int64' } }])
|
|
183
|
+
|
|
184
|
+
const floatProp = findPropertyByName(entity!, 'float_val')
|
|
185
|
+
assert.exists(floatProp)
|
|
186
|
+
assert.equal(floatProp!.type, 'number')
|
|
187
|
+
assert.deepEqual(floatProp!.bindings, [{ type: 'web', schema: { format: 'double' } }])
|
|
188
|
+
})
|
|
189
|
+
})
|