@api-client/core 0.18.62 → 0.18.64
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/API_MODELING_EXPLAINER.md +57 -0
- package/DATA_MODELING_EXPLAINER.md +74 -0
- package/build/src/modeling/DataDomain.d.ts +6 -5
- package/build/src/modeling/DataDomain.d.ts.map +1 -1
- package/build/src/modeling/DataDomain.js +5 -4
- package/build/src/modeling/DataDomain.js.map +1 -1
- package/build/src/modeling/DomainAssociation.js +1 -1
- package/build/src/modeling/DomainAssociation.js.map +1 -1
- package/build/src/modeling/DomainEntity.d.ts +9 -6
- package/build/src/modeling/DomainEntity.d.ts.map +1 -1
- package/build/src/modeling/DomainEntity.js +34 -18
- package/build/src/modeling/DomainEntity.js.map +1 -1
- package/build/src/modeling/ai/Semantics.d.ts +7 -0
- package/build/src/modeling/ai/Semantics.d.ts.map +1 -0
- package/build/src/modeling/ai/Semantics.js +552 -0
- package/build/src/modeling/ai/Semantics.js.map +1 -0
- package/build/src/modeling/definitions/Calculated.d.ts +1 -1
- package/build/src/modeling/definitions/Calculated.js.map +1 -1
- package/build/src/modeling/definitions/Country.d.ts +1 -1
- package/build/src/modeling/definitions/Country.js.map +1 -1
- package/build/src/modeling/definitions/Derived.d.ts +1 -1
- package/build/src/modeling/definitions/Derived.js.map +1 -1
- package/build/src/modeling/definitions/Description.d.ts +0 -1
- package/build/src/modeling/definitions/Description.d.ts.map +1 -1
- package/build/src/modeling/definitions/Description.js.map +1 -1
- package/build/src/modeling/definitions/Email.d.ts +5 -1
- package/build/src/modeling/definitions/Email.d.ts.map +1 -1
- package/build/src/modeling/definitions/Email.js.map +1 -1
- package/build/src/modeling/definitions/HTML.d.ts +0 -22
- package/build/src/modeling/definitions/HTML.d.ts.map +1 -1
- package/build/src/modeling/definitions/HTML.js +0 -1
- package/build/src/modeling/definitions/HTML.js.map +1 -1
- package/build/src/modeling/definitions/Markdown.d.ts +0 -16
- package/build/src/modeling/definitions/Markdown.d.ts.map +1 -1
- package/build/src/modeling/definitions/Markdown.js +0 -1
- package/build/src/modeling/definitions/Markdown.js.map +1 -1
- package/build/src/modeling/helpers/Intelisense.d.ts.map +1 -1
- package/build/src/modeling/helpers/Intelisense.js +2 -2
- package/build/src/modeling/helpers/Intelisense.js.map +1 -1
- package/build/src/modeling/importers/JsonSchemaImporter.js +1 -1
- package/build/src/modeling/importers/JsonSchemaImporter.js.map +1 -1
- package/build/src/modeling/types.d.ts +0 -14
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +15 -15
- package/package.json +4 -4
- package/src/modeling/DataDomain.ts +6 -6
- package/src/modeling/DomainAssociation.ts +1 -1
- package/src/modeling/DomainEntity.ts +36 -18
- package/src/modeling/ai/Semantics.ts +597 -0
- package/src/modeling/ai/readme.md +7 -0
- package/src/modeling/definitions/Calculated.ts +1 -1
- package/src/modeling/definitions/Country.ts +1 -1
- package/src/modeling/definitions/Derived.ts +1 -1
- package/src/modeling/definitions/Description.ts +0 -1
- package/src/modeling/definitions/Email.ts +5 -1
- package/src/modeling/definitions/HTML.ts +0 -23
- package/src/modeling/definitions/Markdown.ts +0 -17
- package/src/modeling/helpers/Intelisense.ts +8 -14
- package/src/modeling/importers/JsonSchemaImporter.ts +1 -1
- package/src/modeling/types.ts +0 -15
- package/tests/unit/modeling/amf/shape_generator.spec.ts +4 -4
- package/tests/unit/modeling/api_model_expose_entity.spec.ts +11 -11
- package/tests/unit/modeling/api_model_remove_entity.spec.ts +1 -1
- package/tests/unit/modeling/data_domain_associations.spec.ts +6 -6
- package/tests/unit/modeling/data_domain_foreign.spec.ts +2 -2
- package/tests/unit/modeling/data_domain_namespaces.spec.ts +1 -1
- package/tests/unit/modeling/data_domain_serialization.spec.ts +15 -15
- package/tests/unit/modeling/domain_asociation.spec.ts +15 -15
- package/tests/unit/modeling/domain_entity.spec.ts +3 -3
- package/tests/unit/modeling/domain_entity_associations.spec.ts +4 -4
- package/tests/unit/modeling/domain_entity_example_generator_json.spec.ts +16 -16
- package/tests/unit/modeling/domain_entity_example_generator_xml.spec.ts +14 -14
- package/tests/unit/modeling/domain_property.spec.ts +1 -1
- package/tests/unit/modeling/domain_validation.spec.ts +3 -3
- package/tests/unit/modeling/domain_versioning.spec.ts +5 -5
- package/tests/unit/modeling/exposed_entity_setter_validation.spec.ts +1 -1
- package/tests/unit/modeling/validation/association_validation.spec.ts +40 -56
- package/tests/unit/modeling/validation/entity_validation.spec.ts +2 -2
|
@@ -3,34 +3,12 @@ import { SemanticType } from '../Semantics.js'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Configuration options for the HTML semantic.
|
|
6
|
-
* Controls rendering, sanitization, and allowed HTML features.
|
|
7
6
|
*/
|
|
8
7
|
export interface HTMLConfig {
|
|
9
8
|
/**
|
|
10
9
|
* List of allowed HTML tags in the content.
|
|
11
10
|
*/
|
|
12
11
|
allowedTags?: string[]
|
|
13
|
-
/**
|
|
14
|
-
* Render mode: 'html', 'text', or 'both'.
|
|
15
|
-
*
|
|
16
|
-
* - `html` - The API returns the HTML content as-is (after sanitization based on your other rules like allowedTags).
|
|
17
|
-
* - `text` - The API returns the text content, stripping out all HTML tags.
|
|
18
|
-
* This is extremely useful for various backend and frontend tasks:
|
|
19
|
-
* - **Search Indexing**: You can easily feed the plain text content into a search engine like Elasticsearch
|
|
20
|
-
* without the noise of HTML tags.
|
|
21
|
-
* - **Content Previews/Summaries**: Displaying a snippet of the content in a list view or a notification
|
|
22
|
-
* where formatting is not desired.
|
|
23
|
-
* - **Compatibility**: Sending the content to systems that don't support HTML, such as plain-text
|
|
24
|
-
* emails or SMS notifications.
|
|
25
|
-
* - `both` - The API returns an object containing both the raw HTML and the plain text version. For example:
|
|
26
|
-
* ```json
|
|
27
|
-
* {
|
|
28
|
-
* "html": "<p>Hello, <strong>world</strong>!</p>",
|
|
29
|
-
* "text": "Hello, world!"
|
|
30
|
-
* }
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
renderMode?: 'html' | 'text' | 'both'
|
|
34
12
|
/**
|
|
35
13
|
* Sanitization level: 'strict', 'moderate', or 'none'.
|
|
36
14
|
*/
|
|
@@ -119,7 +97,6 @@ export const createHTMLSemantic = (config: HTMLConfig = {}): AppliedHTMLSemantic
|
|
|
119
97
|
*/
|
|
120
98
|
export const DEFAULT_HTML_CONFIG: HTMLConfig = {
|
|
121
99
|
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'p', 'br', 'pre', 'code', 'div', 'span'],
|
|
122
|
-
renderMode: 'html',
|
|
123
100
|
sanitizeLevel: 'strict',
|
|
124
101
|
allowImages: true,
|
|
125
102
|
allowLinks: true,
|
|
@@ -10,22 +10,6 @@ export interface MarkdownConfig {
|
|
|
10
10
|
* List of allowed HTML tags in the rendered output.
|
|
11
11
|
*/
|
|
12
12
|
allowedTags?: string[]
|
|
13
|
-
/**
|
|
14
|
-
* Render mode: 'html', 'text', or 'both'.
|
|
15
|
-
* This controls how the stored Markdown content is transformed and returned by the API.
|
|
16
|
-
*
|
|
17
|
-
* - `html` - The API converts the Markdown to HTML and returns the sanitized result.
|
|
18
|
-
* - `text` - The original Markdown as when created, but sanitized.
|
|
19
|
-
* - `both` - The API returns an object containing both the rendered HTML and the plain text version.
|
|
20
|
-
* This provides maximum flexibility for client applications.
|
|
21
|
-
* ```json
|
|
22
|
-
* {
|
|
23
|
-
* "html": "<h1>Title</h1><p>Some <strong>bold</strong> text.</p>",
|
|
24
|
-
* "text": "Title Some bold text."
|
|
25
|
-
* }
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
renderMode?: 'html' | 'text' | 'both'
|
|
29
13
|
/**
|
|
30
14
|
* Sanitization level: 'strict', 'moderate', or 'none'.
|
|
31
15
|
*/
|
|
@@ -110,7 +94,6 @@ export const createMarkdownSemantic = (config: MarkdownConfig = {}): AppliedMark
|
|
|
110
94
|
*/
|
|
111
95
|
export const DEFAULT_MARKDOWN_CONFIG: MarkdownConfig = {
|
|
112
96
|
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'p', 'br', 'pre', 'code'],
|
|
113
|
-
renderMode: 'html',
|
|
114
97
|
sanitizeLevel: 'strict',
|
|
115
98
|
allowImages: true,
|
|
116
99
|
allowLinks: true,
|
|
@@ -325,13 +325,10 @@ export function addOwnerField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
|
325
325
|
if (existing) {
|
|
326
326
|
return existing
|
|
327
327
|
}
|
|
328
|
-
const prop = entity.addAssociation(
|
|
329
|
-
{},
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
required: true,
|
|
333
|
-
}
|
|
334
|
-
)
|
|
328
|
+
const prop = entity.addAssociation({
|
|
329
|
+
info: { name: 'owner', displayName: 'Owner', ...info },
|
|
330
|
+
required: true,
|
|
331
|
+
})
|
|
335
332
|
prop.addSemantic({
|
|
336
333
|
id: SemanticType.ResourceOwnerIdentifier,
|
|
337
334
|
})
|
|
@@ -378,13 +375,10 @@ export function addUpdatedByField(entity: DomainEntity, info: Partial<IThing> =
|
|
|
378
375
|
if (existing) {
|
|
379
376
|
return existing
|
|
380
377
|
}
|
|
381
|
-
const prop = entity.addAssociation(
|
|
382
|
-
{},
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
required: true,
|
|
386
|
-
}
|
|
387
|
-
)
|
|
378
|
+
const prop = entity.addAssociation({
|
|
379
|
+
info: { name: 'updated_by', displayName: 'Updated By', ...info },
|
|
380
|
+
required: true,
|
|
381
|
+
})
|
|
388
382
|
prop.addSemantic({
|
|
389
383
|
id: SemanticType.ResourceOwnerIdentifier,
|
|
390
384
|
})
|
|
@@ -455,7 +455,7 @@ export class JsonSchemaImporter {
|
|
|
455
455
|
// Case 2: If any `$ref` was found, we treat it as an Association.
|
|
456
456
|
if (analysis.refs.size > 0) {
|
|
457
457
|
const sanitizedName = toDatabaseColumnName(propName, 'untitled_association')
|
|
458
|
-
const assoc = entity.addAssociation({
|
|
458
|
+
const assoc = entity.addAssociation({ info: { name: sanitizedName } })
|
|
459
459
|
if (sanitizedName !== propName) {
|
|
460
460
|
assoc.info.displayName = propName // Keep original name for display
|
|
461
461
|
}
|
package/src/modeling/types.ts
CHANGED
|
@@ -33,21 +33,6 @@ export interface DataDomainRemoveOptions {
|
|
|
33
33
|
force?: boolean
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export interface AssociationAddOptions {
|
|
37
|
-
/**
|
|
38
|
-
* When set, it is the the association target's origin data domain.
|
|
39
|
-
* The key of the target data domain where this target entity is defined.
|
|
40
|
-
* Only used when `key` is specified.
|
|
41
|
-
*/
|
|
42
|
-
domain?: string
|
|
43
|
-
/**
|
|
44
|
-
* The key of the association target, if known.
|
|
45
|
-
* If the target of the association is not specified, and the association has no other targets,
|
|
46
|
-
* the association is ignored in the namespace while processing.
|
|
47
|
-
*/
|
|
48
|
-
key?: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
36
|
export interface DomainGraphEdge {
|
|
52
37
|
/**
|
|
53
38
|
* Indicates that the edge is to or from a foreign domain.
|
|
@@ -95,7 +95,7 @@ test.group('entity()', (group) => {
|
|
|
95
95
|
|
|
96
96
|
test('adds associations to the properties', ({ assert }) => {
|
|
97
97
|
e1.addProperty({ key: 'test', info: { name: 'test' } })
|
|
98
|
-
e1.addAssociation({
|
|
98
|
+
e1.addAssociation({ info: { name: 'test association' } })
|
|
99
99
|
const result = e1.toApiShape() as IApiNodeShape
|
|
100
100
|
|
|
101
101
|
assert.lengthOf(result.properties, 2)
|
|
@@ -777,7 +777,7 @@ test.group('associationProperty()', (group) => {
|
|
|
777
777
|
m1 = root.addModel({ key: 'm1' })
|
|
778
778
|
e1 = m1.addEntity({ key: 'e1', info: { name: 'e1' } })
|
|
779
779
|
e2 = m1.addEntity({ key: 'e2', info: { name: 'e2' } })
|
|
780
|
-
a1 = e1.addAssociation({ key: e2.key },
|
|
780
|
+
a1 = e1.addAssociation({ targets: [{ key: e2.key }], key: 'a1', info: { name: 'a1' } })
|
|
781
781
|
e1.addProperty({ key: 'p1', type: 'boolean', info: { name: 'p1' } })
|
|
782
782
|
p2 = e2.addProperty({ key: 'p2', type: 'number', info: { name: 'p2' } })
|
|
783
783
|
})
|
|
@@ -1048,7 +1048,7 @@ test.group('AI Generated tests', (group) => {
|
|
|
1048
1048
|
m1 = root.addModel({ key: 'm1' })
|
|
1049
1049
|
e1 = m1.addEntity({ key: 'e1', info: { name: 'e1' } })
|
|
1050
1050
|
e2 = m1.addEntity({ key: 'e2', info: { name: 'e2' } })
|
|
1051
|
-
a1 = e1.addAssociation({ key: e2.key },
|
|
1051
|
+
a1 = e1.addAssociation({ targets: [{ key: e2.key }], key: 'a1', info: { name: 'a1' } })
|
|
1052
1052
|
p1 = e1.addProperty({ key: 'p1', info: { name: 'p1' } })
|
|
1053
1053
|
})
|
|
1054
1054
|
|
|
@@ -1067,7 +1067,7 @@ test.group('AI Generated tests', (group) => {
|
|
|
1067
1067
|
const e3 = m2.addEntity({ key: 'e3', info: { name: 'e3' } })
|
|
1068
1068
|
fd1.info.version = '1.0.0'
|
|
1069
1069
|
root.registerForeignDomain(fd1)
|
|
1070
|
-
const a2 = e1.addAssociation({ key: e3.key, domain: fd1.key },
|
|
1070
|
+
const a2 = e1.addAssociation({ targets: [{ key: e3.key, domain: fd1.key }], key: 'a2', info: { name: 'a2' } })
|
|
1071
1071
|
const result = e1.toApiShape() as IApiNodeShape
|
|
1072
1072
|
assert.lengthOf(result.properties, 3)
|
|
1073
1073
|
assert.isTrue(result.properties.some((prop) => prop.id === a2.key))
|
|
@@ -72,7 +72,7 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
72
72
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
73
73
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
74
74
|
// Add association from A to B
|
|
75
|
-
eA.addAssociation({
|
|
75
|
+
eA.addAssociation({ info: { name: 'entityB' }, targets: [{ key: eB.key }] })
|
|
76
76
|
const model = new ApiModel()
|
|
77
77
|
model.attachDataDomain(domain)
|
|
78
78
|
const exposedA = model.exposeEntity({ key: eA.key }, { followAssociations: true })
|
|
@@ -90,8 +90,8 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
90
90
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
91
91
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
92
92
|
// A -> B, B -> A
|
|
93
|
-
eA.addAssociation({
|
|
94
|
-
eB.addAssociation({
|
|
93
|
+
eA.addAssociation({ info: { name: 'assocAB' }, targets: [{ key: eB.key }] })
|
|
94
|
+
eB.addAssociation({ info: { name: 'assocBA' }, targets: [{ key: eA.key }] })
|
|
95
95
|
const model = new ApiModel()
|
|
96
96
|
model.attachDataDomain(domain)
|
|
97
97
|
model.exposeEntity({ key: eA.key }, { followAssociations: true, maxDepth: 4 })
|
|
@@ -114,7 +114,7 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
114
114
|
const dm = domain.addModel()
|
|
115
115
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
116
116
|
// A -> A (self-association)
|
|
117
|
-
eA.addAssociation({
|
|
117
|
+
eA.addAssociation({ info: { name: 'assocAA' }, targets: [{ key: eA.key }] })
|
|
118
118
|
const model = new ApiModel()
|
|
119
119
|
model.attachDataDomain(domain)
|
|
120
120
|
model.exposeEntity({ key: eA.key }, { followAssociations: true })
|
|
@@ -132,8 +132,8 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
132
132
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
133
133
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
134
134
|
const eC = dm.addEntity({ info: { name: 'C' } })
|
|
135
|
-
eA.addAssociation({
|
|
136
|
-
eB.addAssociation({
|
|
135
|
+
eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
|
|
136
|
+
eB.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
|
|
137
137
|
const model = new ApiModel()
|
|
138
138
|
model.attachDataDomain(domain)
|
|
139
139
|
model.exposeEntity({ key: eA.key }, { followAssociations: true })
|
|
@@ -152,8 +152,8 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
152
152
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
153
153
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
154
154
|
const eC = dm.addEntity({ info: { name: 'C' } })
|
|
155
|
-
eA.addAssociation({
|
|
156
|
-
eA.addAssociation({
|
|
155
|
+
eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
|
|
156
|
+
eA.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
|
|
157
157
|
const model = new ApiModel()
|
|
158
158
|
model.attachDataDomain(domain)
|
|
159
159
|
model.exposeEntity({ key: eA.key }, { followAssociations: true })
|
|
@@ -173,9 +173,9 @@ test.group('ApiModel.exposeEntity()', () => {
|
|
|
173
173
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
174
174
|
const eC = dm.addEntity({ info: { name: 'C' } })
|
|
175
175
|
const eD = dm.addEntity({ info: { name: 'D' } })
|
|
176
|
-
eA.addAssociation({
|
|
177
|
-
eB.addAssociation({
|
|
178
|
-
eC.addAssociation({
|
|
176
|
+
eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
|
|
177
|
+
eB.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
|
|
178
|
+
eC.addAssociation({ info: { name: 'toD' }, targets: [{ key: eD.key }] })
|
|
179
179
|
const model = new ApiModel()
|
|
180
180
|
model.attachDataDomain(domain)
|
|
181
181
|
model.exposeEntity({ key: eA.key }, { followAssociations: true, maxDepth: 2 })
|
|
@@ -23,7 +23,7 @@ test.group('ApiModel.removeEntity()', () => {
|
|
|
23
23
|
const eA = dm.addEntity({ info: { name: 'A' } })
|
|
24
24
|
const eB = dm.addEntity({ info: { name: 'B' } })
|
|
25
25
|
// A -> B
|
|
26
|
-
eA.addAssociation({
|
|
26
|
+
eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
|
|
27
27
|
const model = new ApiModel()
|
|
28
28
|
model.attachDataDomain(domain)
|
|
29
29
|
const rootExposure = model.exposeEntity({ key: eA.key }, { followAssociations: true })
|
|
@@ -17,7 +17,7 @@ test.group('DataDomain.addAssociation()', () => {
|
|
|
17
17
|
const model = dataDomain.addModel({ key: 'test-model' })
|
|
18
18
|
const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
|
|
19
19
|
const entity2 = dataDomain.addEntity(model.key, { key: 'entity2' })
|
|
20
|
-
const association = dataDomain.addAssociation(entity1.key, { key: entity2.key })
|
|
20
|
+
const association = dataDomain.addAssociation(entity1.key, { targets: [{ key: entity2.key }] })
|
|
21
21
|
assert.instanceOf(association, DomainAssociation)
|
|
22
22
|
assert.isTrue(dataDomain.graph.hasNode(association.key))
|
|
23
23
|
}).tags(['@modeling', '@associations', '@add'])
|
|
@@ -84,7 +84,7 @@ test.group('DataDomain.addAssociation()', () => {
|
|
|
84
84
|
|
|
85
85
|
domain1.registerForeignDomain(domain2)
|
|
86
86
|
|
|
87
|
-
const association = domain1.addAssociation(e1.key, { key: 'entity2', domain: domain2.key })
|
|
87
|
+
const association = domain1.addAssociation(e1.key, { targets: [{ key: 'entity2', domain: domain2.key }] })
|
|
88
88
|
assert.instanceOf(association, DomainAssociation)
|
|
89
89
|
assert.isTrue(domain1.graph.hasNode(association.key))
|
|
90
90
|
}).tags(['@modeling', '@associations', '@add'])
|
|
@@ -99,7 +99,7 @@ test.group('DataDomain.addAssociation()', () => {
|
|
|
99
99
|
domain1.registerForeignDomain(domain2)
|
|
100
100
|
|
|
101
101
|
assert.throws(() => {
|
|
102
|
-
domain1.addAssociation(e1.key, { key: 'non-existent-entity', domain: 'domain2' })
|
|
102
|
+
domain1.addAssociation(e1.key, { targets: [{ key: 'non-existent-entity', domain: 'domain2' }] })
|
|
103
103
|
}, 'Foreign entity domain2:non-existent-entity not found')
|
|
104
104
|
}).tags(['@modeling', '@associations', '@add'])
|
|
105
105
|
})
|
|
@@ -159,7 +159,7 @@ test.group('DataDomain.removeAssociation()', () => {
|
|
|
159
159
|
domain2.info.version = '1.0.0'
|
|
160
160
|
domain1.registerForeignDomain(domain2)
|
|
161
161
|
|
|
162
|
-
const association = domain1.addAssociation(e1.key, { key: e2.key, domain: domain2.key })
|
|
162
|
+
const association = domain1.addAssociation(e1.key, { targets: [{ key: e2.key, domain: domain2.key }] })
|
|
163
163
|
domain1.removeAssociation(association.key)
|
|
164
164
|
assert.isFalse(domain1.graph.hasNode(association.key))
|
|
165
165
|
}).tags(['@modeling', '@associations', '@remove'])
|
|
@@ -187,7 +187,7 @@ test.group('DataDomain.removeAssociation()', () => {
|
|
|
187
187
|
const model = dataDomain.addModel()
|
|
188
188
|
const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
|
|
189
189
|
const entity2 = dataDomain.addEntity(model.key, { key: 'entity2' })
|
|
190
|
-
const association = dataDomain.addAssociation(entity1.key, { key: entity2.key })
|
|
190
|
+
const association = dataDomain.addAssociation(entity1.key, { targets: [{ key: entity2.key }] })
|
|
191
191
|
dataDomain.removeAssociation(association.key)
|
|
192
192
|
assert.isTrue(dataDomain.graph.hasNode(entity1.key))
|
|
193
193
|
assert.isTrue(dataDomain.graph.hasNode(entity2.key))
|
|
@@ -282,7 +282,7 @@ test.group('DataDomain.findAssociation()', () => {
|
|
|
282
282
|
const e2 = domain2.addEntity(m2.key, { key: 'entity2' })
|
|
283
283
|
domain2.info.version = '1.0.0'
|
|
284
284
|
domain1.registerForeignDomain(domain2)
|
|
285
|
-
const association = domain1.addAssociation(e1.key, { key: e2.key, domain: domain2.key })
|
|
285
|
+
const association = domain1.addAssociation(e1.key, { targets: [{ key: e2.key, domain: domain2.key }] })
|
|
286
286
|
|
|
287
287
|
const foundAssociation = domain1.findAssociation(association.key)
|
|
288
288
|
assert.deepEqual(foundAssociation, association)
|
|
@@ -118,7 +118,7 @@ test.group('DataDomain.registerForeignDomain()', () => {
|
|
|
118
118
|
const model = foreignDomain.addModel({ key: 'test-model' })
|
|
119
119
|
const entity1 = foreignDomain.addEntity(model.key, { key: 'entity1' })
|
|
120
120
|
const entity2 = foreignDomain.addEntity(model.key, { key: 'entity2' })
|
|
121
|
-
const association = entity1.addAssociation({ key: entity2.key })
|
|
121
|
+
const association = entity1.addAssociation({ targets: [{ key: entity2.key }] })
|
|
122
122
|
foreignDomain.info.version = '1.0.0'
|
|
123
123
|
dataDomain.registerForeignDomain(foreignDomain)
|
|
124
124
|
|
|
@@ -606,7 +606,7 @@ test.group('DataDomain.findAssociation()', () => {
|
|
|
606
606
|
const m1 = fd.addModel()
|
|
607
607
|
const e1 = m1.addEntity()
|
|
608
608
|
const e2 = m1.addEntity()
|
|
609
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
609
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
610
610
|
fd.info.version = '1.0.0'
|
|
611
611
|
domain.registerForeignDomain(fd)
|
|
612
612
|
const result = domain.findAssociation(a1.key, fd.key)
|
|
@@ -143,7 +143,7 @@ test.group('DataDomain.removeNamespace()', () => {
|
|
|
143
143
|
const e1 = m1.addEntity({ key: 'entity1' })
|
|
144
144
|
const e2 = m1.addEntity({ key: 'entity2' })
|
|
145
145
|
const p1 = e1.addProperty({ key: 'property1' })
|
|
146
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
146
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
147
147
|
d1.removeNamespace(ns1.key)
|
|
148
148
|
assert.isFalse(d1.graph.hasNode(ns1.key))
|
|
149
149
|
assert.isFalse(d1.graph.hasNode(ns2.key))
|
|
@@ -139,7 +139,7 @@ test.group('DataDomain Serialization and Deserialization', () => {
|
|
|
139
139
|
const m1 = domain.addModel()
|
|
140
140
|
const e1 = m1.addEntity()
|
|
141
141
|
const e2 = m1.addEntity()
|
|
142
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
142
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
143
143
|
|
|
144
144
|
const serialized = domain.toJSON()
|
|
145
145
|
const restored = new DataDomain(serialized)
|
|
@@ -168,7 +168,7 @@ test.group('DataDomain Serialization and Deserialization', () => {
|
|
|
168
168
|
e2.addParent(e1.key)
|
|
169
169
|
const p1 = e1.addProperty({ type: 'string' })
|
|
170
170
|
const p2 = e2.addProperty({ type: 'number' })
|
|
171
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
171
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
172
172
|
|
|
173
173
|
const serialized = domain.toJSON()
|
|
174
174
|
const restored = new DataDomain(serialized)
|
|
@@ -249,7 +249,7 @@ test.group('DataDomain Serialization and Deserialization', () => {
|
|
|
249
249
|
domain.registerForeignDomain(fd)
|
|
250
250
|
const m1 = domain.addModel({ key: 'm1' })
|
|
251
251
|
const e1 = m1.addEntity({ key: 'e1' })
|
|
252
|
-
const a1 = e1.addAssociation({ key: fe1.key, domain: fd.key }
|
|
252
|
+
const a1 = e1.addAssociation({ key: 'a1', targets: [{ key: fe1.key, domain: fd.key }] })
|
|
253
253
|
const serialized = domain.toJSON()
|
|
254
254
|
const restored = new DataDomain(serialized, [fd])
|
|
255
255
|
|
|
@@ -278,7 +278,7 @@ test.group('DataDomain Serialization and Deserialization', () => {
|
|
|
278
278
|
domain.registerForeignDomain(fd)
|
|
279
279
|
const m1 = domain.addModel()
|
|
280
280
|
const e1 = m1.addEntity()
|
|
281
|
-
const a1 = e1.addAssociation({ key: fe1.key, domain: fd.key })
|
|
281
|
+
const a1 = e1.addAssociation({ targets: [{ key: fe1.key, domain: fd.key }] })
|
|
282
282
|
|
|
283
283
|
const serialized = domain.toJSON()
|
|
284
284
|
const restored = new DataDomain(serialized)
|
|
@@ -311,7 +311,7 @@ test.group('DataDomain Serialization and Deserialization', () => {
|
|
|
311
311
|
// Add properties and associations
|
|
312
312
|
const p1 = e1.addProperty({ type: 'string', key: 'p1' })
|
|
313
313
|
const p2 = e2.addProperty({ type: 'number', key: 'p2' })
|
|
314
|
-
const a1 = e1.addAssociation({ key: e3.key },
|
|
314
|
+
const a1 = e1.addAssociation({ targets: [{ key: e3.key }], key: 'a1' })
|
|
315
315
|
|
|
316
316
|
const serialized = domain.toJSON()
|
|
317
317
|
const restored = new DataDomain(serialized)
|
|
@@ -411,7 +411,7 @@ test.group('Validation Tests', () => {
|
|
|
411
411
|
const m1 = domain.addModel()
|
|
412
412
|
const e1 = m1.addEntity()
|
|
413
413
|
const e2 = m1.addEntity()
|
|
414
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
414
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
415
415
|
|
|
416
416
|
// Manually break the graph by removing the association edge to parent
|
|
417
417
|
domain.graph.removeEdge(e1.key, a1.key)
|
|
@@ -461,7 +461,7 @@ test.group('Validation Tests', () => {
|
|
|
461
461
|
const m1 = domain.addModel()
|
|
462
462
|
const e1 = m1.addEntity()
|
|
463
463
|
const e2 = m1.addEntity()
|
|
464
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
464
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
465
465
|
|
|
466
466
|
// Remove the association target edge
|
|
467
467
|
domain.graph.removeEdge(a1.key, e2.key)
|
|
@@ -478,7 +478,7 @@ test.group('Validation Tests', () => {
|
|
|
478
478
|
const m1 = domain.addModel()
|
|
479
479
|
const e1 = m1.addEntity()
|
|
480
480
|
const e2 = m1.addEntity()
|
|
481
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
481
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
482
482
|
|
|
483
483
|
// Remove the target entity but keep the edge pointing to it
|
|
484
484
|
domain.graph.removeNode(e2.key)
|
|
@@ -493,7 +493,7 @@ test.group('Validation Tests', () => {
|
|
|
493
493
|
const m1 = domain.addModel()
|
|
494
494
|
const e1 = m1.addEntity()
|
|
495
495
|
const e2 = m1.addEntity()
|
|
496
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
496
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
497
497
|
|
|
498
498
|
// Remove the proper target and make it point to the model instead
|
|
499
499
|
domain.graph.removeEdge(a1.key, e2.key)
|
|
@@ -512,7 +512,7 @@ test.group('Validation Tests', () => {
|
|
|
512
512
|
const e1 = m1.addEntity()
|
|
513
513
|
const e2 = m2.addEntity()
|
|
514
514
|
e1.addProperty({ type: 'string' })
|
|
515
|
-
e1.addAssociation({ key: e2.key })
|
|
515
|
+
e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
516
516
|
|
|
517
517
|
// This should not throw any validation errors
|
|
518
518
|
assert.doesNotThrow(() => {
|
|
@@ -529,7 +529,7 @@ test.group('Validation Tests', () => {
|
|
|
529
529
|
const e1 = m1.addEntity()
|
|
530
530
|
const e2 = m2.addEntity()
|
|
531
531
|
e1.addProperty({ type: 'string' })
|
|
532
|
-
e1.addAssociation({ key: e2.key })
|
|
532
|
+
e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
533
533
|
|
|
534
534
|
// This should not throw any validation errors
|
|
535
535
|
assert.doesNotThrow(() => {
|
|
@@ -547,7 +547,7 @@ test.group('Validation Tests', () => {
|
|
|
547
547
|
domain.registerForeignDomain(fd)
|
|
548
548
|
const m1 = domain.addModel()
|
|
549
549
|
const e1 = m1.addEntity()
|
|
550
|
-
e1.addAssociation({ key: fe1.key, domain: fd.key })
|
|
550
|
+
e1.addAssociation({ targets: [{ key: fe1.key, domain: fd.key }] })
|
|
551
551
|
|
|
552
552
|
// Foreign associations should pass validation
|
|
553
553
|
assert.doesNotThrow(() => {
|
|
@@ -630,7 +630,7 @@ test.group('DataDomain Lenient Mode Deserialization', () => {
|
|
|
630
630
|
const m1 = domain.addModel()
|
|
631
631
|
const e1 = m1.addEntity()
|
|
632
632
|
const e2 = m1.addEntity()
|
|
633
|
-
const a1 = e1.addAssociation({ key: e2.key })
|
|
633
|
+
const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
634
634
|
|
|
635
635
|
const serialized = domain.toJSON()
|
|
636
636
|
|
|
@@ -701,7 +701,7 @@ test.group('DataDomain Lenient Mode Deserialization', () => {
|
|
|
701
701
|
domain.registerForeignDomain(fd)
|
|
702
702
|
const m1 = domain.addModel()
|
|
703
703
|
const e1 = m1.addEntity()
|
|
704
|
-
e1.addAssociation({ key: fe1.key, domain: fd.key })
|
|
704
|
+
e1.addAssociation({ targets: [{ key: fe1.key, domain: fd.key }] })
|
|
705
705
|
|
|
706
706
|
const serialized = domain.toJSON()
|
|
707
707
|
|
|
@@ -762,7 +762,7 @@ test.group('DataDomain Lenient Mode Deserialization', () => {
|
|
|
762
762
|
const e1 = m1.addEntity()
|
|
763
763
|
const e2 = m1.addEntity()
|
|
764
764
|
const p1 = e1.addProperty({ type: 'string' })
|
|
765
|
-
e1.addAssociation({ key: e2.key })
|
|
765
|
+
e1.addAssociation({ targets: [{ key: e2.key }] })
|
|
766
766
|
|
|
767
767
|
const serialized = domain.toJSON()
|
|
768
768
|
|
|
@@ -916,7 +916,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
916
916
|
const entity = model.addEntity()
|
|
917
917
|
const targetEntity = model.addEntity()
|
|
918
918
|
|
|
919
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
919
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
920
920
|
association.info.name = 'originalAssociation'
|
|
921
921
|
association.required = true
|
|
922
922
|
association.multiple = false
|
|
@@ -938,7 +938,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
938
938
|
|
|
939
939
|
// Add multiple fields to test ordering
|
|
940
940
|
const property1 = entity.addProperty({ info: { name: 'first' } })
|
|
941
|
-
const association1 = entity.addAssociation({ key: targetEntity.key })
|
|
941
|
+
const association1 = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
942
942
|
association1.info.name = 'originalAssoc'
|
|
943
943
|
const property2 = entity.addProperty({ info: { name: 'second' } })
|
|
944
944
|
|
|
@@ -959,10 +959,10 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
959
959
|
const entity = model.addEntity()
|
|
960
960
|
const targetEntity = model.addEntity()
|
|
961
961
|
|
|
962
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
962
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
963
963
|
association.info.name = 'test'
|
|
964
964
|
// Add another association that conflicts with the first generated name
|
|
965
|
-
const conflictAssoc = entity.addAssociation({ key: targetEntity.key })
|
|
965
|
+
const conflictAssoc = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
966
966
|
conflictAssoc.info.name = 'test_copy'
|
|
967
967
|
|
|
968
968
|
const duplicate = association.duplicate()
|
|
@@ -976,7 +976,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
976
976
|
const entity = model.addEntity()
|
|
977
977
|
const targetEntity = model.addEntity()
|
|
978
978
|
|
|
979
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
979
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
980
980
|
// The association gets the default name "new_association" from schema creation
|
|
981
981
|
assert.equal(association.info.name, 'new_association')
|
|
982
982
|
|
|
@@ -991,7 +991,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
991
991
|
const entity = model.addEntity()
|
|
992
992
|
const targetEntity = model.addEntity()
|
|
993
993
|
|
|
994
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
994
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
995
995
|
association.info.name = 'test'
|
|
996
996
|
// Manually set name to undefined to test the fallback logic
|
|
997
997
|
association.info.name = undefined
|
|
@@ -1007,7 +1007,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1007
1007
|
const entity = model.addEntity()
|
|
1008
1008
|
const targetEntity = model.addEntity()
|
|
1009
1009
|
|
|
1010
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1010
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1011
1011
|
const originalKey = association.key
|
|
1012
1012
|
|
|
1013
1013
|
const duplicate = association.duplicate()
|
|
@@ -1022,7 +1022,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1022
1022
|
const targetEntity1 = model.addEntity()
|
|
1023
1023
|
const targetEntity2 = model.addEntity()
|
|
1024
1024
|
|
|
1025
|
-
const association = entity.addAssociation({ key: targetEntity1.key })
|
|
1025
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity1.key }] })
|
|
1026
1026
|
association.addTarget(targetEntity2.key)
|
|
1027
1027
|
association.info.name = 'multiTarget'
|
|
1028
1028
|
|
|
@@ -1047,7 +1047,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1047
1047
|
const entity = model.addEntity()
|
|
1048
1048
|
const targetEntity = model.addEntity()
|
|
1049
1049
|
|
|
1050
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1050
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1051
1051
|
association.info.name = 'complexAssociation'
|
|
1052
1052
|
association.info.description = 'A complex association'
|
|
1053
1053
|
association.required = true
|
|
@@ -1077,7 +1077,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1077
1077
|
const entity = model.addEntity()
|
|
1078
1078
|
const targetEntity = model.addEntity()
|
|
1079
1079
|
|
|
1080
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1080
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1081
1081
|
association.info.name = 'original'
|
|
1082
1082
|
association.schema = { linked: false, unionType: 'anyOf' }
|
|
1083
1083
|
association.bindings = [{ type: 'web', schema: { hidden: false } }]
|
|
@@ -1134,7 +1134,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1134
1134
|
const entity = model.addEntity()
|
|
1135
1135
|
const targetEntity = model.addEntity()
|
|
1136
1136
|
|
|
1137
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1137
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1138
1138
|
association.info.name = 'test'
|
|
1139
1139
|
|
|
1140
1140
|
let notificationCalled = false
|
|
@@ -1184,7 +1184,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1184
1184
|
const model = dataDomain.addModel()
|
|
1185
1185
|
const entity = model.addEntity()
|
|
1186
1186
|
const targetEntity = model.addEntity()
|
|
1187
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1187
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1188
1188
|
association.info.name = 'only'
|
|
1189
1189
|
|
|
1190
1190
|
const duplicate = association.duplicate()
|
|
@@ -1201,7 +1201,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1201
1201
|
const targetEntity = model.addEntity()
|
|
1202
1202
|
|
|
1203
1203
|
const property1 = entity.addProperty({ info: { name: 'prop1' } })
|
|
1204
|
-
const association1 = entity.addAssociation({ key: targetEntity.key })
|
|
1204
|
+
const association1 = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1205
1205
|
association1.info.name = 'assoc1'
|
|
1206
1206
|
const property2 = entity.addProperty({ info: { name: 'prop2' } })
|
|
1207
1207
|
|
|
@@ -1217,7 +1217,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1217
1217
|
const model = dataDomain.addModel()
|
|
1218
1218
|
const entity = model.addEntity()
|
|
1219
1219
|
const targetEntity = model.addEntity()
|
|
1220
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
1220
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
1221
1221
|
|
|
1222
1222
|
const duplicate = association.duplicate()
|
|
1223
1223
|
|
|
@@ -1268,7 +1268,7 @@ test.group('DomainAssociation.duplicate()', () => {
|
|
|
1268
1268
|
const entity = model.addEntity()
|
|
1269
1269
|
dataDomain.registerForeignDomain(fd)
|
|
1270
1270
|
|
|
1271
|
-
const association = entity.addAssociation({ key: fe1.key, domain: fd.key })
|
|
1271
|
+
const association = entity.addAssociation({ targets: [{ key: fe1.key, domain: fd.key }] })
|
|
1272
1272
|
|
|
1273
1273
|
const duplicate = association.duplicate()
|
|
1274
1274
|
|
|
@@ -785,7 +785,7 @@ test.group('DomainEntity.generateUniqueName()', () => {
|
|
|
785
785
|
const targetEntity = model.addEntity()
|
|
786
786
|
|
|
787
787
|
// Add an association that conflicts with the first generated name
|
|
788
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
788
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
789
789
|
association.info.name = 'test_copy'
|
|
790
790
|
|
|
791
791
|
const uniqueName = entity.generateUniqueName('test')
|
|
@@ -800,7 +800,7 @@ test.group('DomainEntity.generateUniqueName()', () => {
|
|
|
800
800
|
|
|
801
801
|
// Add property and association with conflicting names
|
|
802
802
|
entity.addProperty({ info: { name: 'test_copy' } })
|
|
803
|
-
const association = entity.addAssociation({ key: targetEntity.key })
|
|
803
|
+
const association = entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
804
804
|
association.info.name = 'test_copy_2'
|
|
805
805
|
|
|
806
806
|
const uniqueName = entity.generateUniqueName('test')
|
|
@@ -816,7 +816,7 @@ test.group('DomainEntity.generateUniqueName()', () => {
|
|
|
816
816
|
// Add property without a name (undefined name)
|
|
817
817
|
entity.addProperty({ info: {} })
|
|
818
818
|
// Add association without a name
|
|
819
|
-
entity.addAssociation({ key: targetEntity.key })
|
|
819
|
+
entity.addAssociation({ targets: [{ key: targetEntity.key }] })
|
|
820
820
|
|
|
821
821
|
const uniqueName = entity.generateUniqueName('test')
|
|
822
822
|
assert.equal(uniqueName, 'test_copy')
|