@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.
Files changed (31) hide show
  1. package/build/src/modeling/ApiModel.d.ts +5 -5
  2. package/build/src/modeling/ApiModel.d.ts.map +1 -1
  3. package/build/src/modeling/ApiModel.js +8 -8
  4. package/build/src/modeling/ApiModel.js.map +1 -1
  5. package/build/src/modeling/helpers/database.d.ts +24 -0
  6. package/build/src/modeling/helpers/database.d.ts.map +1 -0
  7. package/build/src/modeling/helpers/database.js +42 -0
  8. package/build/src/modeling/helpers/database.js.map +1 -0
  9. package/build/src/modeling/importers/CsvImporter.d.ts +36 -0
  10. package/build/src/modeling/importers/CsvImporter.d.ts.map +1 -0
  11. package/build/src/modeling/importers/CsvImporter.js +81 -0
  12. package/build/src/modeling/importers/CsvImporter.js.map +1 -0
  13. package/build/src/modeling/importers/ImporterException.d.ts +10 -0
  14. package/build/src/modeling/importers/ImporterException.d.ts.map +1 -0
  15. package/build/src/modeling/importers/ImporterException.js +10 -0
  16. package/build/src/modeling/importers/ImporterException.js.map +1 -0
  17. package/build/src/modeling/importers/JsonSchemaImporter.d.ts +99 -0
  18. package/build/src/modeling/importers/JsonSchemaImporter.d.ts.map +1 -0
  19. package/build/src/modeling/importers/JsonSchemaImporter.js +525 -0
  20. package/build/src/modeling/importers/JsonSchemaImporter.js.map +1 -0
  21. package/build/tsconfig.tsbuildinfo +1 -1
  22. package/data/models/example-generator-api.json +13 -13
  23. package/package.json +5 -2
  24. package/src/modeling/ApiModel.ts +11 -10
  25. package/src/modeling/helpers/database.ts +48 -0
  26. package/src/modeling/importers/CsvImporter.ts +88 -0
  27. package/src/modeling/importers/ImporterException.ts +10 -0
  28. package/src/modeling/importers/JsonSchemaImporter.ts +642 -0
  29. package/tests/unit/modeling/api_model.spec.ts +9 -9
  30. package/tests/unit/modeling/importers/csv_importer.spec.ts +189 -0
  31. 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.userKey)
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
- userKey: 'user-entity',
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.equal(schema.userKey, 'user-entity')
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.userKey)
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
- userKey: 'user-entity',
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.equal(model.userKey, 'user-entity')
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.userKey)
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
- userKey: 'user-entity',
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.equal(json.userKey, 'user-entity')
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
+ })