Skip to content

Commit

Permalink
Fix serialization of custom properties
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-fleck-at committed Aug 19, 2024
1 parent d425bd0 commit d5b3067
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 33 deletions.
12 changes: 6 additions & 6 deletions examples/mapping-example/ExampleDWH/mappings/DWH.mapping.cm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mapping:
- Customer
conditions:
- CalcAgeSourceObject.BirthDate = Customer.BirthDate
customProperties:
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleMappingSource
Expand All @@ -58,14 +58,14 @@ mapping:
expression: "Fixed String"
- attribute: Today
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleEntityTargetAttribute
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleEntityTargetAttribute
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleMappingTarget
customProperties:
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleMapping
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ systemDiagram:
width: 159.649658203125
height: 96
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleNode
- name: Author
value: "CrossBreeze"
- name: ExampleNode
edges:
- id: CustomerToOrder
relationship: Order_Customer
Expand All @@ -35,10 +35,10 @@ systemDiagram:
sourceNode: AddressNode
targetNode: CustomerNode
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleEdge
- name: Author
value: "CrossBreeze"
- name: ExampleEdge
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleDiagram
- name: Author
value: "CrossBreeze"
- name: ExampleDiagram
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ entity:
name: "CountryCode"
datatype: "Text"
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleEntityAttribute
- name: Author
value: "CrossBreeze"
- name: ExampleEntityAttribute
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleEntity
- name: Author
value: "CrossBreeze"
- name: ExampleEntity
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ relationship:
- parent: Customer.Id
child: ExampleCRM.Address.CustomerID
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleRelationshipAttribute
- name: Author
value: "CrossBreeze"
- name: ExampleRelationshipAttribute
customProperties:
- name: Author
value: "CrossBreeze"
- name: ExampleRelationship
- name: Author
value: "CrossBreeze"
- name: ExampleRelationship
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const PROPERTY_ORDER = [
'source',
'conditions',
'expression',
'customProperties'
'customProperties',
'value'
];

const ID_OR_IDREF = [
Expand All @@ -64,7 +65,6 @@ const ID_OR_IDREF = [
'sources',
'target',
'attribute',
'from',
'join',
'conditions',
'parent',
Expand Down Expand Up @@ -96,13 +96,23 @@ export class CrossModelSerializer implements Serializer<CrossModelRoot> {
return this.serializeValue(newRoot, CrossModelSerializer.INDENTATION_AMOUNT_OBJECT * -1, 'root');
}

private serializeValue(value: any, indentationLevel: number, key: string): string {
private serializeValue(value: any, indentationLevel: number, key: string, container?: Record<string, any>): string {
if (this.isCustomProperty(container)) {
// custom properties need a special handling because they use properties that are already used differently in other places
// 'name:' typically used as a string but used as an identifier in the custom property
// 'value:' used for ID-based references in other places but used as a string value in the custom property
return key === 'name' ? value : JSON.stringify(value);
}

if (Array.isArray(value)) {
return this.serializeArray(value, indentationLevel, key);
} else if (typeof value === 'object' && value !== undefined) {
return this.serializeObject(value, indentationLevel + CrossModelSerializer.INDENTATION_AMOUNT_OBJECT, key);
} else if (this.isIdOrIdRef(key)) {
// for IDs and references (based on IDs) we guarantee that they do not contain spaces or other characters that break the string
return value;
} else {
return this.isIdOrIdRef(key) ? value : JSON.stringify(value);
return JSON.stringify(value);
}
}

Expand All @@ -117,14 +127,16 @@ export class CrossModelSerializer implements Serializer<CrossModelRoot> {
.sort((left, right) => PROPERTY_ORDER.indexOf(left[0]) - PROPERTY_ORDER.indexOf(right[0]))
.map(([objKey, objValue]) => {
if (Array.isArray(objValue) && objValue.length === 0) {
// skip empty arrays
return;
}
if (objKey === 'identifier' && objValue === false) {
// skip false identifiers for better readability
return;
}

const propKey = this.serializeKey(objKey);
const propValue = this.serializeValue(objValue, indentationLevel, propKey);
const propValue = this.serializeValue(objValue, indentationLevel, propKey, obj);
if (typeof objValue === 'object') {
return `${indentation}${propKey}:${CrossModelSerializer.CHAR_NEWLINE}${propValue}`;
} else {
Expand All @@ -143,7 +155,7 @@ export class CrossModelSerializer implements Serializer<CrossModelRoot> {
private serializeArray(arr: any[], indentationLevel: number, key: string): string {
const serializedItems = arr
.filter(item => item !== undefined)
.map(item => this.serializeValue(item, indentationLevel, key))
.map(item => this.serializeValue(item, indentationLevel, key, arr))
.map(item => this.ensureArrayItem(item, indentationLevel + CrossModelSerializer.INDENTATION_AMOUNT_ARRAY))
.join(CrossModelSerializer.CHAR_NEWLINE);
return serializedItems;
Expand Down Expand Up @@ -214,4 +226,14 @@ export class CrossModelSerializer implements Serializer<CrossModelRoot> {
const right = obj.expression.right.$type === StringLiteral ? quote(obj.expression.right.value) : obj.expression.right.value;
return [left, obj.expression.op, right].join(' ');
}

private isCustomProperty(obj: any): obj is { name: string; value?: string } {
// we need to be strict here cause other objects may also have name or value properties
return (
typeof obj === 'object' &&
'name' in obj &&
typeof (obj as any).name === 'string' &&
(Object.keys(obj).length === 1 || (Object.keys(obj).length === 2 && 'value' in obj && typeof (obj as any).value === 'string'))
);
}
}

0 comments on commit d5b3067

Please sign in to comment.