@api-client/core 0.18.3 → 0.18.5
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/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 +41 -0
- package/build/src/modeling/importers/CsvImporter.d.ts.map +1 -0
- package/build/src/modeling/importers/CsvImporter.js +82 -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/package.json +5 -2
- package/src/modeling/helpers/database.ts +48 -0
- package/src/modeling/importers/CsvImporter.ts +91 -0
- package/src/modeling/importers/ImporterException.ts +10 -0
- package/src/modeling/importers/JsonSchemaImporter.ts +642 -0
- package/tests/unit/modeling/importers/csv_importer.spec.ts +189 -0
- package/tests/unit/modeling/importers/json_schema_importer.spec.ts +451 -0
|
@@ -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
|
+
})
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { test } from '@japa/runner'
|
|
3
|
+
// @ts-expect-error - No types for this package
|
|
4
|
+
import schemas from 'schema-org-json-schemas'
|
|
5
|
+
import { type InMemorySchema, JsonSchemaImporter } from '../../../../src/modeling/importers/JsonSchemaImporter.js'
|
|
6
|
+
import { DataDomain } from '../../../../src/modeling/DataDomain.js'
|
|
7
|
+
import type { JSONSchema7 } from 'json-schema'
|
|
8
|
+
import type { DomainEntity } from '../../../../src/modeling/DomainEntity.js'
|
|
9
|
+
import { DomainAssociation, DomainProperty } from '../../../../src/browser.js'
|
|
10
|
+
|
|
11
|
+
const input: InMemorySchema[] = []
|
|
12
|
+
for (const [key, value] of Object.entries(schemas)) {
|
|
13
|
+
input.push({
|
|
14
|
+
path: key,
|
|
15
|
+
contents: value as JSONSchema7,
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function findEntityByName(domain: DataDomain, name: string): DomainEntity {
|
|
20
|
+
for (const entity of domain.listEntities()) {
|
|
21
|
+
if (entity.info.name === name || entity.info.displayName === name) {
|
|
22
|
+
return entity
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`Entity with name "${name}" not found`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function findPropertyByName(entity: DomainEntity, name: string): DomainProperty {
|
|
29
|
+
for (const prop of entity.listProperties()) {
|
|
30
|
+
if (prop.info.name === name || prop.info.displayName === name) {
|
|
31
|
+
return prop
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Property with name "${name}" not found`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function findAssociationByName(entity: DomainEntity, name: string): DomainAssociation {
|
|
38
|
+
for (const assoc of entity.listAssociations()) {
|
|
39
|
+
if (assoc.info.name === name || assoc.info.displayName === name) {
|
|
40
|
+
return assoc
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Association with name "${name}" not found`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test.group('JsonSchemaImporter', (g) => {
|
|
47
|
+
let domain: DataDomain
|
|
48
|
+
|
|
49
|
+
g.setup(async () => {
|
|
50
|
+
domain = new DataDomain({ info: { name: 'Test Domain' } })
|
|
51
|
+
const importer = new JsonSchemaImporter(domain)
|
|
52
|
+
await importer.import(input, 'imported_model')
|
|
53
|
+
// console.log('Import report:', result.messages)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// We won't test for all schemas, just a few to ensure the importer works
|
|
57
|
+
|
|
58
|
+
test('Book schema is imported', ({ assert }) => {
|
|
59
|
+
const bookEntity = findEntityByName(domain, 'Book')
|
|
60
|
+
assert.exists(bookEntity, 'Book entity should exist')
|
|
61
|
+
assert.equal(bookEntity.info.name, 'book')
|
|
62
|
+
assert.equal(bookEntity.info.displayName, 'Book')
|
|
63
|
+
assert.equal(bookEntity.info.description, 'A book.')
|
|
64
|
+
const abridged = findPropertyByName(bookEntity, 'abridged')
|
|
65
|
+
assert.exists(abridged, 'Book entity should have abridged property')
|
|
66
|
+
assert.isUndefined(abridged.info.displayName, 'displayName should be undefined.')
|
|
67
|
+
assert.equal(abridged.info.description, 'Indicates whether the book is an abridged edition.')
|
|
68
|
+
assert.equal(abridged.type, 'boolean')
|
|
69
|
+
assert.isFalse(abridged.required, 'Abridged property should not be required')
|
|
70
|
+
assert.isFalse(abridged.multiple, 'Abridged property should not allow multiple values')
|
|
71
|
+
const bookEdition = findPropertyByName(bookEntity, 'bookEdition')
|
|
72
|
+
assert.exists(bookEdition, 'Book entity should have bookEdition property')
|
|
73
|
+
assert.equal(bookEdition.info.displayName, 'bookEdition')
|
|
74
|
+
assert.equal(bookEdition.info.description, 'The edition of the book.')
|
|
75
|
+
assert.equal(bookEdition.type, 'string')
|
|
76
|
+
assert.isFalse(bookEdition.required, 'Book edition property should not be required')
|
|
77
|
+
assert.isFalse(bookEdition.multiple, 'Book edition property should not allow multiple values')
|
|
78
|
+
const bookFormat = findPropertyByName(bookEntity, 'bookFormat')
|
|
79
|
+
assert.exists(bookFormat, 'Book entity should have bookFormat property')
|
|
80
|
+
assert.equal(bookFormat.info.name, 'book_format')
|
|
81
|
+
assert.equal(bookFormat.info.displayName, 'bookFormat')
|
|
82
|
+
assert.equal(bookFormat.info.description, 'The format of the book.')
|
|
83
|
+
assert.isFalse(bookFormat.required, 'Book format property should not be required')
|
|
84
|
+
assert.isFalse(bookFormat.multiple, 'Book format property should not allow multiple values')
|
|
85
|
+
const illustrator = findAssociationByName(bookEntity, 'illustrator')
|
|
86
|
+
assert.exists(illustrator, 'Book entity should have illustrator association')
|
|
87
|
+
assert.equal(illustrator.info.name, 'illustrator')
|
|
88
|
+
assert.isUndefined(illustrator.info.displayName, 'displayName should be undefined.')
|
|
89
|
+
assert.equal(illustrator.info.description, 'The illustrator of the book.')
|
|
90
|
+
assert.isFalse(illustrator.required, 'Illustrator association should not be required')
|
|
91
|
+
assert.isFalse(illustrator.multiple, 'Illustrator association should not allow multiple values')
|
|
92
|
+
assert.isNotEmpty(illustrator.targets, 'Illustrator association should have targets')
|
|
93
|
+
assert.lengthOf([...illustrator.listTargets()], 1, 'Illustrator association should have exactly one target')
|
|
94
|
+
const isbn = findPropertyByName(bookEntity, 'isbn')
|
|
95
|
+
assert.exists(isbn, 'Book entity should have isbn property')
|
|
96
|
+
assert.equal(isbn.info.name, 'isbn')
|
|
97
|
+
assert.isUndefined(isbn.info.displayName, 'The ISBN display name is undefined.')
|
|
98
|
+
assert.equal(isbn.info.description, 'The ISBN of the book.')
|
|
99
|
+
assert.equal(isbn.type, 'string')
|
|
100
|
+
assert.isFalse(isbn.required, 'ISBN property should not be required')
|
|
101
|
+
assert.isFalse(isbn.multiple, 'ISBN property should not allow multiple values')
|
|
102
|
+
const numberOfPages = findPropertyByName(bookEntity, 'numberOfPages')
|
|
103
|
+
assert.exists(numberOfPages, 'Book entity should have numberOfPages property')
|
|
104
|
+
assert.equal(numberOfPages.info.name, 'number_of_pages')
|
|
105
|
+
assert.equal(numberOfPages.info.displayName, 'numberOfPages')
|
|
106
|
+
assert.equal(numberOfPages.info.description, 'The number of pages in the book.')
|
|
107
|
+
assert.equal(numberOfPages.type, 'number')
|
|
108
|
+
assert.isFalse(numberOfPages.required, 'Number of pages property should not be required')
|
|
109
|
+
assert.isFalse(numberOfPages.multiple, 'Number of pages property should not allow multiple values')
|
|
110
|
+
const numberOfPagesWebBindings = numberOfPages.getWebBinding()
|
|
111
|
+
assert.exists(numberOfPagesWebBindings, 'Number of pages should have web bindings')
|
|
112
|
+
assert.equal(numberOfPagesWebBindings?.format, 'int64', 'Web binding format should be int64')
|
|
113
|
+
const parents = [...bookEntity.parents]
|
|
114
|
+
assert.lengthOf(parents, 1, 'Book entity should have one parent')
|
|
115
|
+
assert.equal(parents[0].info.name, 'creative_work', 'Parent entity should be CreativeWork')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('Canal schema is imported and inherits from BodyOfWater', ({ assert }) => {
|
|
119
|
+
const canalEntity = findEntityByName(domain, 'Canal')
|
|
120
|
+
assert.exists(canalEntity, 'Canal entity should exist')
|
|
121
|
+
assert.equal(canalEntity.info.name, 'canal')
|
|
122
|
+
assert.equal(canalEntity.info.displayName, 'Canal')
|
|
123
|
+
assert.equal(canalEntity.info.description, 'A canal, like the Panama Canal.')
|
|
124
|
+
// Should have one parent: BodyOfWater
|
|
125
|
+
const parents = [...canalEntity.parents]
|
|
126
|
+
assert.lengthOf(parents, 1, 'Canal entity should have one parent')
|
|
127
|
+
assert.equal(parents[0].info.name, 'body_of_water', 'Parent entity should be BodyOfWater')
|
|
128
|
+
assert.equal(parents[0].info.displayName, 'BodyOfWater')
|
|
129
|
+
assert.equal(parents[0].info.description, 'A body of water, such as a sea, ocean, or lake.')
|
|
130
|
+
// Canal should not have properties or associations of its own (unless inherited)
|
|
131
|
+
assert.lengthOf([...canalEntity.listProperties()], 0, 'Canal should not have direct properties')
|
|
132
|
+
assert.lengthOf([...canalEntity.listAssociations()], 0, 'Canal should not have direct associations')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('DataFeedItem schema is imported and properties are mapped correctly', ({ assert }) => {
|
|
136
|
+
const dataFeedItem = findEntityByName(domain, 'DataFeedItem')
|
|
137
|
+
assert.exists(dataFeedItem, 'DataFeedItem entity should exist')
|
|
138
|
+
assert.equal(dataFeedItem.info.name, 'data_feed_item')
|
|
139
|
+
assert.equal(dataFeedItem.info.displayName, 'DataFeedItem')
|
|
140
|
+
assert.equal(dataFeedItem.info.description, 'A single item within a larger data feed.')
|
|
141
|
+
// Should have one parent: Intangible
|
|
142
|
+
const parents = [...dataFeedItem.parents]
|
|
143
|
+
assert.lengthOf(parents, 1, 'DataFeedItem should have one parent')
|
|
144
|
+
assert.equal(parents[0].info.name, 'intangible', 'Parent entity should be Intangible')
|
|
145
|
+
|
|
146
|
+
// dateCreated property
|
|
147
|
+
const dateCreated = findPropertyByName(dataFeedItem, 'dateCreated')
|
|
148
|
+
assert.exists(dateCreated, 'dateCreated property should exist')
|
|
149
|
+
assert.equal(dateCreated.info.name, 'date_created')
|
|
150
|
+
assert.equal(dateCreated.info.displayName, 'dateCreated')
|
|
151
|
+
assert.equal(
|
|
152
|
+
dateCreated.info.description,
|
|
153
|
+
'The date on which the CreativeWork was created or the item was added to a DataFeed.'
|
|
154
|
+
)
|
|
155
|
+
assert.equal(dateCreated.type, 'datetime')
|
|
156
|
+
assert.isFalse(dateCreated.required, 'dateCreated should not be required')
|
|
157
|
+
assert.isFalse(dateCreated.multiple, 'dateCreated should not allow multiple values')
|
|
158
|
+
|
|
159
|
+
// dateDeleted property
|
|
160
|
+
const dateDeleted = findPropertyByName(dataFeedItem, 'dateDeleted')
|
|
161
|
+
assert.exists(dateDeleted, 'dateDeleted property should exist')
|
|
162
|
+
assert.equal(dateDeleted.info.name, 'date_deleted')
|
|
163
|
+
assert.equal(dateDeleted.info.displayName, 'dateDeleted')
|
|
164
|
+
assert.equal(dateDeleted.info.description, 'The datetime the item was removed from the DataFeed.')
|
|
165
|
+
assert.equal(dateDeleted.type, 'datetime')
|
|
166
|
+
assert.isFalse(dateDeleted.required, 'dateDeleted should not be required')
|
|
167
|
+
assert.isFalse(dateDeleted.multiple, 'dateDeleted should not allow multiple values')
|
|
168
|
+
|
|
169
|
+
// dateModified property
|
|
170
|
+
const dateModified = findPropertyByName(dataFeedItem, 'dateModified')
|
|
171
|
+
assert.exists(dateModified, 'dateModified property should exist')
|
|
172
|
+
assert.equal(dateModified.info.name, 'date_modified')
|
|
173
|
+
assert.equal(dateModified.info.displayName, 'dateModified')
|
|
174
|
+
assert.equal(
|
|
175
|
+
dateModified.info.description,
|
|
176
|
+
"The date on which the CreativeWork was most recently modified or when the item's entry was modified within a DataFeed."
|
|
177
|
+
)
|
|
178
|
+
assert.equal(dateModified.type, 'datetime')
|
|
179
|
+
assert.isFalse(dateModified.required, 'dateModified should not be required')
|
|
180
|
+
assert.isFalse(dateModified.multiple, 'dateModified should not allow multiple values')
|
|
181
|
+
|
|
182
|
+
// item association
|
|
183
|
+
const itemAssoc = findAssociationByName(dataFeedItem, 'item')
|
|
184
|
+
assert.exists(itemAssoc, 'item association should exist')
|
|
185
|
+
assert.equal(itemAssoc.info.name, 'item')
|
|
186
|
+
assert.isUndefined(itemAssoc.info.displayName, 'the item association display name should be undefined.')
|
|
187
|
+
assert.equal(
|
|
188
|
+
itemAssoc.info.description,
|
|
189
|
+
"An entity represented by an entry in a list or data feed (e.g. an 'artist' in a list of 'artists')’."
|
|
190
|
+
)
|
|
191
|
+
assert.isFalse(itemAssoc.required, 'item association should not be required')
|
|
192
|
+
assert.isTrue(itemAssoc.multiple, 'item association should allow multiple values (array or single)')
|
|
193
|
+
assert.isNotEmpty(itemAssoc.targets, 'item association should have targets')
|
|
194
|
+
assert.lengthOf([...itemAssoc.listTargets()], 1, 'item association should have exactly one target')
|
|
195
|
+
// Target should be Thing
|
|
196
|
+
const target = [...itemAssoc.listTargets()][0]
|
|
197
|
+
assert.equal(target.info.name, 'thing', 'item association target should be Thing')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('Drug schema is imported and properties are mapped correctly', ({ assert }) => {
|
|
201
|
+
const drugEntity = findEntityByName(domain, 'Drug')
|
|
202
|
+
assert.exists(drugEntity, 'Drug entity should exist')
|
|
203
|
+
assert.equal(drugEntity.info.name, 'drug')
|
|
204
|
+
assert.equal(drugEntity.info.displayName, 'Drug')
|
|
205
|
+
assert.equal(
|
|
206
|
+
drugEntity.info.description,
|
|
207
|
+
'A chemical or biologic substance, used as a medical therapy, that has a physiological effect on an organism. Here the term drug is used interchangeably with the term medicine although clinical knowledge make a clear difference between them.'
|
|
208
|
+
)
|
|
209
|
+
// Should have one parent: Substance
|
|
210
|
+
const parents = [...drugEntity.parents]
|
|
211
|
+
assert.lengthOf(parents, 1, 'Drug should have one parent')
|
|
212
|
+
assert.equal(parents[0].info.name, 'substance', 'Parent entity should be Substance')
|
|
213
|
+
|
|
214
|
+
// activeIngredient property
|
|
215
|
+
const activeIngredient = findPropertyByName(drugEntity, 'activeIngredient')
|
|
216
|
+
assert.exists(activeIngredient, 'activeIngredient property should exist')
|
|
217
|
+
assert.equal(activeIngredient.info.name, 'active_ingredient')
|
|
218
|
+
assert.equal(activeIngredient.info.displayName, 'activeIngredient')
|
|
219
|
+
assert.equal(
|
|
220
|
+
activeIngredient.info.description,
|
|
221
|
+
'An active ingredient, typically chemical compounds and/or biologic substances.'
|
|
222
|
+
)
|
|
223
|
+
assert.equal(activeIngredient.type, 'string')
|
|
224
|
+
assert.isFalse(activeIngredient.required, 'activeIngredient should not be required')
|
|
225
|
+
assert.isTrue(activeIngredient.multiple, 'activeIngredient should allow multiple values')
|
|
226
|
+
|
|
227
|
+
// administrationRoute property
|
|
228
|
+
const administrationRoute = findPropertyByName(drugEntity, 'administrationRoute')
|
|
229
|
+
assert.exists(administrationRoute, 'administrationRoute property should exist')
|
|
230
|
+
assert.equal(administrationRoute.info.name, 'administration_route')
|
|
231
|
+
assert.equal(administrationRoute.info.displayName, 'administrationRoute')
|
|
232
|
+
assert.equal(administrationRoute.info.description, "A route by which this drug may be administered, e.g. 'oral'.")
|
|
233
|
+
assert.equal(administrationRoute.type, 'string')
|
|
234
|
+
assert.isFalse(administrationRoute.required, 'administrationRoute should not be required')
|
|
235
|
+
assert.isTrue(administrationRoute.multiple, 'administrationRoute should allow multiple values')
|
|
236
|
+
|
|
237
|
+
// alcoholWarning property
|
|
238
|
+
const alcoholWarning = findPropertyByName(drugEntity, 'alcoholWarning')
|
|
239
|
+
assert.exists(alcoholWarning, 'alcoholWarning property should exist')
|
|
240
|
+
assert.equal(alcoholWarning.info.name, 'alcohol_warning')
|
|
241
|
+
assert.equal(alcoholWarning.info.displayName, 'alcoholWarning')
|
|
242
|
+
assert.equal(
|
|
243
|
+
alcoholWarning.info.description,
|
|
244
|
+
'Any precaution, guidance, contraindication, etc. related to consumption of alcohol while taking this drug.'
|
|
245
|
+
)
|
|
246
|
+
assert.equal(alcoholWarning.type, 'string')
|
|
247
|
+
assert.isFalse(alcoholWarning.required, 'alcoholWarning should not be required')
|
|
248
|
+
assert.isFalse(alcoholWarning.multiple, 'alcoholWarning should not allow multiple values')
|
|
249
|
+
|
|
250
|
+
// availableStrength association
|
|
251
|
+
const availableStrength = findAssociationByName(drugEntity, 'availableStrength')
|
|
252
|
+
assert.exists(availableStrength, 'availableStrength association should exist')
|
|
253
|
+
assert.equal(availableStrength.info.name, 'available_strength')
|
|
254
|
+
assert.equal(availableStrength.info.displayName, 'availableStrength')
|
|
255
|
+
assert.equal(availableStrength.info.description, 'An available dosage strength for the drug.')
|
|
256
|
+
assert.isFalse(availableStrength.required, 'availableStrength should not be required')
|
|
257
|
+
assert.isTrue(availableStrength.multiple, 'availableStrength should allow multiple values')
|
|
258
|
+
assert.isNotEmpty(availableStrength.targets, 'availableStrength should have targets')
|
|
259
|
+
|
|
260
|
+
// isAvailableGenerically property
|
|
261
|
+
const isAvailableGenerically = findPropertyByName(drugEntity, 'isAvailableGenerically')
|
|
262
|
+
assert.exists(isAvailableGenerically, 'isAvailableGenerically property should exist')
|
|
263
|
+
assert.equal(isAvailableGenerically.info.name, 'is_available_generically')
|
|
264
|
+
assert.equal(isAvailableGenerically.info.displayName, 'isAvailableGenerically')
|
|
265
|
+
assert.equal(
|
|
266
|
+
isAvailableGenerically.info.description,
|
|
267
|
+
'True if the drug is available in a generic form (regardless of name).'
|
|
268
|
+
)
|
|
269
|
+
assert.equal(isAvailableGenerically.type, 'boolean')
|
|
270
|
+
assert.isFalse(isAvailableGenerically.required, 'isAvailableGenerically should not be required')
|
|
271
|
+
assert.isFalse(isAvailableGenerically.multiple, 'isAvailableGenerically should not allow multiple values')
|
|
272
|
+
|
|
273
|
+
// manufacturer association
|
|
274
|
+
const manufacturer = findAssociationByName(drugEntity, 'manufacturer')
|
|
275
|
+
assert.exists(manufacturer, 'manufacturer association should exist')
|
|
276
|
+
assert.equal(manufacturer.info.name, 'manufacturer')
|
|
277
|
+
assert.isUndefined(manufacturer.info.displayName, 'the manufacturer association display name should be undefined.')
|
|
278
|
+
assert.equal(manufacturer.info.description, 'The manufacturer of the product.')
|
|
279
|
+
assert.isFalse(manufacturer.required, 'manufacturer should not be required')
|
|
280
|
+
assert.isFalse(manufacturer.multiple, 'manufacturer should not allow multiple values')
|
|
281
|
+
assert.isNotEmpty(manufacturer.targets, 'manufacturer should have targets')
|
|
282
|
+
|
|
283
|
+
// warning property
|
|
284
|
+
const warning = findPropertyByName(drugEntity, 'warning')
|
|
285
|
+
assert.exists(warning, 'warning property should exist')
|
|
286
|
+
assert.equal(warning.info.name, 'warning')
|
|
287
|
+
assert.isUndefined(warning.info.displayName, 'the warning property display name should be undefined.')
|
|
288
|
+
assert.equal(warning.info.description, 'Any FDA or other warnings about the drug (text or URL).')
|
|
289
|
+
assert.equal(warning.type, 'string')
|
|
290
|
+
assert.isFalse(warning.required, 'warning should not be required')
|
|
291
|
+
assert.isFalse(warning.multiple, 'warning should not allow multiple values')
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
test('Enumeration schema with recursive reference is imported', ({ assert }) => {
|
|
295
|
+
const enumerationEntity = findEntityByName(domain, 'Enumeration')
|
|
296
|
+
assert.exists(enumerationEntity, 'Enumeration entity should exist')
|
|
297
|
+
assert.equal(enumerationEntity.info.name, 'enumeration')
|
|
298
|
+
assert.equal(enumerationEntity.info.displayName, 'Enumeration')
|
|
299
|
+
assert.equal(
|
|
300
|
+
enumerationEntity.info.description,
|
|
301
|
+
'Lists or enumerations—for example, a list of cuisines or music genres, etc.'
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
// Parent should be Intangible
|
|
305
|
+
const parents = [...enumerationEntity.parents]
|
|
306
|
+
assert.lengthOf(parents, 1, 'Enumeration entity should have one parent')
|
|
307
|
+
assert.equal(parents[0].info.name, 'intangible', 'Parent entity should be Intangible')
|
|
308
|
+
|
|
309
|
+
// supersededBy association
|
|
310
|
+
const supersededBy = findAssociationByName(enumerationEntity, 'supersededBy')
|
|
311
|
+
assert.exists(supersededBy, 'supersededBy association should exist')
|
|
312
|
+
assert.equal(supersededBy.info.name, 'superseded_by')
|
|
313
|
+
assert.equal(supersededBy.info.displayName, 'supersededBy')
|
|
314
|
+
assert.equal(
|
|
315
|
+
supersededBy.info.description,
|
|
316
|
+
'Relates a term (i.e. a property, class or enumeration) to one that supersedes it.'
|
|
317
|
+
)
|
|
318
|
+
assert.isFalse(supersededBy.required, 'supersededBy should not be required')
|
|
319
|
+
assert.isTrue(supersededBy.multiple, 'supersededBy should allow multiple values')
|
|
320
|
+
|
|
321
|
+
const targets = [...supersededBy.listTargets()].map((t) => t.info.displayName).sort()
|
|
322
|
+
assert.deepEqual(
|
|
323
|
+
targets,
|
|
324
|
+
['Class', 'Enumeration', 'Property'],
|
|
325
|
+
'Targets should be Class, Enumeration, and Property'
|
|
326
|
+
)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test('Event schema with complex properties is imported', ({ assert }) => {
|
|
330
|
+
const eventEntity = findEntityByName(domain, 'Event')
|
|
331
|
+
assert.exists(eventEntity, 'Event entity should exist')
|
|
332
|
+
assert.equal(eventEntity.info.name, 'event')
|
|
333
|
+
assert.equal(eventEntity.info.displayName, 'Event')
|
|
334
|
+
|
|
335
|
+
// Parent should be Thing
|
|
336
|
+
const parents = [...eventEntity.parents]
|
|
337
|
+
assert.lengthOf(parents, 1, 'Event entity should have one parent')
|
|
338
|
+
assert.equal(parents[0].info.name, 'thing', 'Parent entity should be Thing')
|
|
339
|
+
|
|
340
|
+
// Test a simple boolean property
|
|
341
|
+
const isAccessibleForFree = findPropertyByName(eventEntity, 'isAccessibleForFree')
|
|
342
|
+
assert.exists(isAccessibleForFree)
|
|
343
|
+
assert.equal(isAccessibleForFree.type, 'boolean')
|
|
344
|
+
assert.isFalse(isAccessibleForFree.multiple)
|
|
345
|
+
|
|
346
|
+
// Test a property with a union of string with time formats
|
|
347
|
+
const doorTime = findPropertyByName(eventEntity, 'doorTime')
|
|
348
|
+
assert.exists(doorTime)
|
|
349
|
+
assert.equal(doorTime.type, 'datetime')
|
|
350
|
+
assert.equal(doorTime.info.description, 'The time admission will commence.')
|
|
351
|
+
|
|
352
|
+
const endDate = findPropertyByName(eventEntity, 'endDate')
|
|
353
|
+
assert.exists(endDate)
|
|
354
|
+
assert.equal(endDate.type, 'datetime')
|
|
355
|
+
|
|
356
|
+
// Test a simple association
|
|
357
|
+
const about = findAssociationByName(eventEntity, 'about')
|
|
358
|
+
assert.exists(about)
|
|
359
|
+
assert.isFalse(about.multiple)
|
|
360
|
+
const aboutTargets = [...about.listTargets()]
|
|
361
|
+
assert.lengthOf(aboutTargets, 1)
|
|
362
|
+
assert.equal(aboutTargets[0].info.displayName, 'Thing')
|
|
363
|
+
|
|
364
|
+
// Test an association that can be single or an array
|
|
365
|
+
const actor = findAssociationByName(eventEntity, 'actor')
|
|
366
|
+
assert.exists(actor)
|
|
367
|
+
assert.isTrue(actor.multiple, 'actor should allow multiple values')
|
|
368
|
+
const actorTargets = [...actor.listTargets()]
|
|
369
|
+
assert.lengthOf(actorTargets, 1)
|
|
370
|
+
assert.equal(actorTargets[0].info.displayName, 'Person')
|
|
371
|
+
|
|
372
|
+
// Test an association that can be one of multiple types (and also an array)
|
|
373
|
+
const attendee = findAssociationByName(eventEntity, 'attendee')
|
|
374
|
+
assert.exists(attendee)
|
|
375
|
+
assert.isTrue(attendee.multiple, 'attendee should allow multiple values')
|
|
376
|
+
const attendeeTargets = [...attendee.listTargets()].map((t) => t.info.displayName).sort()
|
|
377
|
+
assert.deepEqual(attendeeTargets, ['Organization', 'Person'])
|
|
378
|
+
|
|
379
|
+
// Test a recursive association
|
|
380
|
+
const subEvent = findAssociationByName(eventEntity, 'subEvent')
|
|
381
|
+
assert.exists(subEvent)
|
|
382
|
+
assert.isTrue(subEvent.multiple, 'subEvent should allow multiple values')
|
|
383
|
+
const subEventTargets = [...subEvent.listTargets()]
|
|
384
|
+
assert.lengthOf(subEventTargets, 1)
|
|
385
|
+
assert.equal(subEventTargets[0].info.displayName, 'Event', 'Target should be self (Event)')
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
test('imports MusicAlbum enum properties correctly', ({ assert }) => {
|
|
389
|
+
const musicAlbumEntity = findEntityByName(domain, 'MusicAlbum')
|
|
390
|
+
const albumProductionType = findPropertyByName(musicAlbumEntity, 'albumProductionType')
|
|
391
|
+
assert.isTrue(albumProductionType.multiple, 'albumProductionType can be an array')
|
|
392
|
+
assert.deepEqual(albumProductionType.schema?.enum?.sort(), [
|
|
393
|
+
'CompilationAlbum',
|
|
394
|
+
'DJMixAlbum',
|
|
395
|
+
'DemoAlbum',
|
|
396
|
+
'LiveAlbum',
|
|
397
|
+
'MixtapeAlbum',
|
|
398
|
+
'RemixAlbum',
|
|
399
|
+
'SoundtrackAlbum',
|
|
400
|
+
'SpokenWordAlbum',
|
|
401
|
+
'StudioAlbum',
|
|
402
|
+
])
|
|
403
|
+
|
|
404
|
+
const albumReleaseType = findPropertyByName(musicAlbumEntity, 'albumReleaseType')
|
|
405
|
+
assert.isFalse(!!albumReleaseType.multiple, 'albumReleaseType is a single value')
|
|
406
|
+
assert.deepEqual(albumReleaseType.schema?.enum?.sort(), [
|
|
407
|
+
'AlbumRelease',
|
|
408
|
+
'BroadcastRelease',
|
|
409
|
+
'EPRelease',
|
|
410
|
+
'SingleRelease',
|
|
411
|
+
])
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
test('imports Brewery correctly', ({ assert }) => {
|
|
415
|
+
const breweryEntity = findEntityByName(domain, 'Brewery')
|
|
416
|
+
assert.lengthOf([...breweryEntity.properties], 0, 'Brewery should not have direct properties')
|
|
417
|
+
assert.lengthOf([...breweryEntity.associations], 0, 'Brewery should not have direct associations')
|
|
418
|
+
assert.equal(breweryEntity.info.name, 'brewery')
|
|
419
|
+
assert.equal(breweryEntity.info.displayName, 'Brewery')
|
|
420
|
+
assert.lengthOf([...breweryEntity.parents], 1, 'Brewery should have one parent')
|
|
421
|
+
assert.equal(
|
|
422
|
+
[...breweryEntity.parents][0].info.name,
|
|
423
|
+
'food_establishment',
|
|
424
|
+
'Parent entity should be FoodEstablishment'
|
|
425
|
+
)
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
test('imports ComedyEvent correctly', ({ assert }) => {
|
|
429
|
+
const comedyEventEntity = findEntityByName(domain, 'ComedyEvent')
|
|
430
|
+
assert.lengthOf([...comedyEventEntity.properties], 0, 'ComedyEvent should not have direct properties')
|
|
431
|
+
assert.lengthOf([...comedyEventEntity.associations], 0, 'ComedyEvent should not have direct associations')
|
|
432
|
+
assert.equal(comedyEventEntity.info.name, 'comedy_event')
|
|
433
|
+
assert.equal(comedyEventEntity.info.displayName, 'ComedyEvent')
|
|
434
|
+
assert.lengthOf([...comedyEventEntity.parents], 1, 'ComedyEvent should have one parent')
|
|
435
|
+
assert.equal([...comedyEventEntity.parents][0].info.name, 'event', 'Parent entity should be Event')
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
test('imports ComicIssue correctly', ({ assert }) => {
|
|
439
|
+
const comicIssueEntity = findEntityByName(domain, 'ComicIssue')
|
|
440
|
+
assert.lengthOf([...comicIssueEntity.properties], 1, 'ComicIssue should have one direct property')
|
|
441
|
+
assert.lengthOf([...comicIssueEntity.associations], 5, 'ComicIssue should have five direct associations')
|
|
442
|
+
assert.equal(comicIssueEntity.info.name, 'comic_issue')
|
|
443
|
+
assert.equal(comicIssueEntity.info.displayName, 'ComicIssue')
|
|
444
|
+
assert.lengthOf([...comicIssueEntity.parents], 1, 'ComicIssue should have one parent')
|
|
445
|
+
assert.equal(
|
|
446
|
+
[...comicIssueEntity.parents][0].info.name,
|
|
447
|
+
'publication_issue',
|
|
448
|
+
'Parent entity should be PublicationIssue'
|
|
449
|
+
)
|
|
450
|
+
})
|
|
451
|
+
})
|