From 355a7a6c93650c454139db9739d8d3319cd708ec Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 21 Aug 2020 13:29:12 +0200 Subject: [PATCH 1/9] Started working on converting from class definition --- src/SparqlResultConverter.ts | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts index b272603..75ae5eb 100644 --- a/src/SparqlResultConverter.ts +++ b/src/SparqlResultConverter.ts @@ -5,19 +5,33 @@ import { ArrayUtil, SparqlResultLine } from "./ArrayUtil"; // Maps the query result of "select_allModules" to an array of Modules export class SparqlResultConverter { - // convertToClass(inputArray: SparqlResultLine[], type: (new () => T)): T{ - // let propertyNames: string[]; - // try { - // const obj = new type(); - // propertyNames = Object.getOwnPropertyNames(obj); - // }catch (err) { - // throw new Error("Error while creating an instance of your type. Make sure 'type' is a valid class with a default constrcutor."); - // } - // propertyNames.forEach(propName => { - - // }); - // return; - // } + + convertToClass(inputArray: SparqlResultLine[], initialProperty: string, type: (new () => T)): T{ + let mappingDefinition: MappingDefinition; + mappingDefinition.propertyToGroup = initialProperty; + mappingDefinition.name = initialProperty; + mappingDefinition.rootName = `${initialProperty}s` + let propertyNames: string[]; + try { + const obj = new type(); + propertyNames = Object.getOwnPropertyNames(obj); + }catch (err) { + throw new Error("Error while creating an instance of your type. Make sure 'type' is a valid class with a default constrcutor."); + } + //TODO: + // If a property type is simple, it can be collected -> push to toCollect + // If a property type is another class, there has to be a new childmapping + // But what about arrays of (both simple and complex) properties? + propertyNames.forEach(propName => { + if(typeof propName == ("string" || "number" || "boolean")) { + mappingDefinition.toCollect.push(propName); + } else { + mappingDefinition. + } + + }); + return; + } convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): Record> { const flattenedArray = ArrayUtil.extractValues(inputArray); From bd66044eec9ad4be6fe67ff69ab99fa9bf51f40b Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 21 Aug 2020 13:29:12 +0200 Subject: [PATCH 2/9] Started working on converting from class definition --- src/SparqlResultConverter.ts | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts index 4eda8f1..83d120d 100644 --- a/src/SparqlResultConverter.ts +++ b/src/SparqlResultConverter.ts @@ -3,12 +3,35 @@ import { ArrayUtil, SparqlResultLine } from "./ArrayUtil"; export class SparqlResultConverter { - /** - * Convert a SPARQL result according to an array of MappingDefinition - * @param inputArray The SPARQL result (result.bindings) - * @param mappingDefinitions An array of mapping definitions - */ - public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): Record> { + // TODO: Continue here + // convertToClass(inputArray: SparqlResultLine[], initialProperty: string, type: (new () => T)): T{ + // let mappingDefinition: MappingDefinition; + // mappingDefinition.propertyToGroup = initialProperty; + // mappingDefinition.name = initialProperty; + // mappingDefinition.rootName = `${initialProperty}s` + // let propertyNames: string[]; + // try { + // const obj = new type(); + // propertyNames = Object.getOwnPropertyNames(obj); + // }catch (err) { + // throw new Error("Error while creating an instance of your type. Make sure 'type' is a valid class with a default constrcutor."); + // } + // //TODO: + // // If a property type is simple, it can be collected -> push to toCollect + // // If a property type is another class, there has to be a new childmapping + // // But what about arrays of (both simple and complex) properties? + // propertyNames.forEach(propName => { + // if(typeof propName == ("string" || "number" || "boolean")) { + // mappingDefinition.toCollect.push(propName); + // } else { + // mappingDefinition. + // } + + // }); + // return; + // } + + convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): Record> { const flattenedArray = ArrayUtil.extractValues(inputArray); return this.convert(flattenedArray, mappingDefinitions); } From 54e4789ba994e528082559890127f88ba85a7e1d Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 4 Sep 2020 15:52:46 +0200 Subject: [PATCH 3/9] New way to access properties to maintain similar result compared to GraphDB --- src/SparqlResultConverter.ts | 31 +++++++++++++++++-- .../testConverting.test.ts | 4 +-- tests/onePropertyTests/testConverting.test.ts | 10 +++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts index 83d120d..1a7e0fb 100644 --- a/src/SparqlResultConverter.ts +++ b/src/SparqlResultConverter.ts @@ -3,6 +3,8 @@ import { ArrayUtil, SparqlResultLine } from "./ArrayUtil"; export class SparqlResultConverter { + outputObject = {}; + // TODO: Continue here // convertToClass(inputArray: SparqlResultLine[], initialProperty: string, type: (new () => T)): T{ // let mappingDefinition: MappingDefinition; @@ -31,9 +33,10 @@ export class SparqlResultConverter { // return; // } - convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): Record> { + public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): SparqlResultConverter { const flattenedArray = ArrayUtil.extractValues(inputArray); - return this.convert(flattenedArray, mappingDefinitions); + this.outputObject = this.convert(flattenedArray, mappingDefinitions); + return this; } @@ -113,6 +116,30 @@ export class SparqlResultConverter { return outputObject; } + /** + * Returns the grouped array of the first root element. Can be used as an easy getter if there is only one root element + */ + public getFirstRootElement(): Array{ + const keys = Object.keys(this.outputObject); + return this.get(keys[0]); + } + + public getAll(): Record> { + return this.outputObject; + } + + /** + * Returns the grouped array with a given key + * @param key A key of one of the root elements + */ + public get(key: string): Array { + if(this.outputObject[key]) { + return this.outputObject[key]; + } else { + return []; + } + } + /** * Groups an array using lodash's groupBy diff --git a/tests/multiplePropertyTests/testConverting.test.ts b/tests/multiplePropertyTests/testConverting.test.ts index c0be336..5562050 100644 --- a/tests/multiplePropertyTests/testConverting.test.ts +++ b/tests/multiplePropertyTests/testConverting.test.ts @@ -29,7 +29,7 @@ describe('One-Layer-Test with multiple properties on first layer', () => { }], }]; - const convertedResult = resultConverter.convertToDefinition(oneLayerTestData.results, oneLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(oneLayerTestData.results, oneLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion failed...'); }); }); @@ -58,7 +58,7 @@ describe('Two-Layer-Test with multiple properties on the second layer', () => { }, ]; - const convertedResult = resultConverter.convertToDefinition(twoLayerTestData.results, twoLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(twoLayerTestData.results, twoLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedTwoLayerResult, 'Testing one layer conversion failed...'); }); }); diff --git a/tests/onePropertyTests/testConverting.test.ts b/tests/onePropertyTests/testConverting.test.ts index aa42017..11fe2c8 100644 --- a/tests/onePropertyTests/testConverting.test.ts +++ b/tests/onePropertyTests/testConverting.test.ts @@ -21,7 +21,7 @@ describe('One Layer Test', () => { } ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedSimpleOneLayerResult, 'Testing one layer conversion without property-collection failed...'); }); @@ -39,7 +39,7 @@ describe('One Layer Test', () => { } ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion without property-collection failed...'); }); @@ -57,7 +57,7 @@ describe('One Layer Test', () => { } ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedOneLayerResultWithCollectedProperty, 'Testing one layer conversion without property-collection failed...'); }); @@ -83,7 +83,7 @@ describe('Two Layer Test', () => { ] }, ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedTwoLayerResult, 'Testing two layer conversion without property collection failed...'); }); @@ -109,7 +109,7 @@ describe('Two Layer Test', () => { ] }, ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition); + const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedTwoLayerResultWithCollectedProperty, 'Testing two layer conversion with property collection failed...'); From 4b032d51054a844710598143130169d7161ac24a Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:31:17 +0200 Subject: [PATCH 4/9] Added another test for keeping / removing ungrouped child elements --- tests/onePropertyTests/expectedResults.ts | 18 +- tests/onePropertyTests/testConverting.test.ts | 204 ++++++++++-------- 2 files changed, 126 insertions(+), 96 deletions(-) diff --git a/tests/onePropertyTests/expectedResults.ts b/tests/onePropertyTests/expectedResults.ts index fb7ed5b..c98def6 100644 --- a/tests/onePropertyTests/expectedResults.ts +++ b/tests/onePropertyTests/expectedResults.ts @@ -1,5 +1,5 @@ -// Expected result for a very simple one layer mapping: -export const expectedSimpleOneLayerResult = { +// Expected result for a very simple one layer mapping including non-grouped children elements: +export const oneLayerResultWithChildren = { owners: [ { ownerName: 'Peter', @@ -48,6 +48,20 @@ export const expectedSimpleOneLayerResult = { } ]}; +// Expected result for a very simple one layer mapping without non-grouped children elements: +export const oneLayerResultWithoutChildren = { + owners: [ + { + ownerName: 'Peter', + }, + { + ownerName: 'John', + }, + { + ownerName: 'Mary', + } + ]}; + export const expectedOneLayerResult = { owners: [ { diff --git a/tests/onePropertyTests/testConverting.test.ts b/tests/onePropertyTests/testConverting.test.ts index 11fe2c8..8fe84cf 100644 --- a/tests/onePropertyTests/testConverting.test.ts +++ b/tests/onePropertyTests/testConverting.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai'; import { expectedOneLayerResult, expectedTwoLayerResult, expectedOneLayerResultWithCollectedProperty, expectedTwoLayerResultWithCollectedProperty, - expectedSimpleOneLayerResult } from './expectedResults'; + oneLayerResultWithChildren } from './expectedResults'; import { SparqlResultConverter, MappingDefinition } from "../../src/SparqlResultConverter"; @@ -9,109 +9,125 @@ import {testData} from './test-data'; const resultConverter = new SparqlResultConverter(); +describe('Tests including just one property on each layer', () => { + describe('One Layer Test', () => { + it('Should group a result on one layer without anything else, keeping all ungrouped child elements', () => { + // Object that defines the structure of the result + const oneLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + } + ]; + + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); + assert.deepEqual(convertedResult, oneLayerResultWithChildren, + 'Testing one layer conversion without property-collection failed...'); + }); -describe('One Layer Test', () => { - it('Should group a result on one layer without anything else', () => { + it('Should group a result on one layer without anything else, removing all ungrouped child elements', () => { // Object that defines the structure of the result - const oneLayerMappingDefinition: MappingDefinition[] = [ - { - rootName: 'owners', - propertyToGroup: 'owner', - name: 'ownerName', - } - ]; - - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); - assert.deepEqual(convertedResult, expectedSimpleOneLayerResult, - 'Testing one layer conversion without property-collection failed...'); - }); + const oneLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + } + ]; - it('Should group a result on one layer with a rootName for the children', () => { + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); + assert.deepEqual(convertedResult, oneLayerResultWithChildren, + 'Testing one layer conversion without property-collection failed...'); + }); + + it('Should group a result on one layer with a rootName for the children', () => { // Object that defines the structure of the result - const oneLayerMappingDefinition: MappingDefinition[] = [ - { - rootName: 'owners', - propertyToGroup: 'owner', - name: 'ownerName', - childMappings: [{ - rootName: 'pets', - }] - } - ]; - - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); - assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion without property-collection failed...'); - }); + const oneLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + childMappings: [{ + rootName: 'pets', + }] + } + ]; + + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); + assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion without property-collection failed...'); + }); - it('Should group a result on one layer and collect a property', () => { + it('Should group a result on one layer and collect a property', () => { // Object that defines the structure of the result - const oneLayerMappingDefinition: MappingDefinition[] = [ - { - rootName: 'owners', - propertyToGroup: 'owner', - name: 'ownerName', - toCollect: ['ownerAge'], - childMappings: [{ - rootName: 'pets', - }] - } - ]; - - const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); - assert.deepEqual(convertedResult, expectedOneLayerResultWithCollectedProperty, - 'Testing one layer conversion without property-collection failed...'); + const oneLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + toCollect: ['ownerAge'], + childMappings: [{ + rootName: 'pets', + }] + } + ]; + + const convertedResult = resultConverter.convertToDefinition(testData.results, oneLayerMappingDefinition).getAll(); + assert.deepEqual(convertedResult, expectedOneLayerResultWithCollectedProperty, + 'Testing one layer conversion without property-collection failed...'); + }); }); -}); -describe('Two Layer Test', () => { - it('Should group a result on two layers', () => { + describe('Two Layer Test', () => { + it('Should group a result on two layers', () => { // Object that defines the structure of the result - const twoLayerMappingDefinition: MappingDefinition[] = [ - { - rootName: 'owners', - propertyToGroup: 'owner', - name: 'ownerName', - childMappings: [ - { - rootName: 'petTypes', - propertyToGroup: 'petType', - name: 'type', - childMappings: [{ - rootName: 'pets' - }] - } - ] - }, - ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); - - assert.deepEqual(convertedResult, expectedTwoLayerResult, 'Testing two layer conversion without property collection failed...'); - }); + const twoLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + childMappings: [ + { + rootName: 'petTypes', + propertyToGroup: 'petType', + name: 'type', + childMappings: [{ + rootName: 'pets' + }] + } + ] + }, + ]; + const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); - it('Should group a result on two layers and collect a property', () => { + assert.deepEqual(convertedResult, expectedTwoLayerResult, 'Testing two layer conversion without property collection failed...'); + }); + + it('Should group a result on two layers and collect a property', () => { // Object that defines the structure of the result - const twoLayerMappingDefinition: MappingDefinition[] = [ - { - rootName: 'owners', - propertyToGroup: 'owner', - name: 'ownerName', - toCollect: ['ownerAge'], - childMappings: [ - { - rootName: 'petTypes', - propertyToGroup: 'petType', - name: 'type', - childMappings: [{ - rootName: 'pets', - - }] - } - ] - }, - ]; - const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); - - assert.deepEqual(convertedResult, expectedTwoLayerResultWithCollectedProperty, - 'Testing two layer conversion with property collection failed...'); + const twoLayerMappingDefinition: MappingDefinition[] = [ + { + rootName: 'owners', + propertyToGroup: 'owner', + name: 'ownerName', + toCollect: ['ownerAge'], + childMappings: [ + { + rootName: 'petTypes', + propertyToGroup: 'petType', + name: 'type', + childMappings: [{ + rootName: 'pets', + + }] + } + ] + }, + ]; + const convertedResult = resultConverter.convertToDefinition(testData.results, twoLayerMappingDefinition).getAll(); + + assert.deepEqual(convertedResult, expectedTwoLayerResultWithCollectedProperty, + 'Testing two layer conversion with property collection failed...'); + }); }); }); From c4971314c191a1fea5af7a03a6845bbd48c7abb4 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:31:49 +0200 Subject: [PATCH 5/9] Added boolean parameter to decide whether or not to keep ungrouped child elements --- src/SparqlResultConverter.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts index 1a7e0fb..bb1f2e1 100644 --- a/src/SparqlResultConverter.ts +++ b/src/SparqlResultConverter.ts @@ -33,9 +33,9 @@ export class SparqlResultConverter { // return; // } - public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[]): SparqlResultConverter { + public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[], keepUngroupedContents = true): SparqlResultConverter { const flattenedArray = ArrayUtil.extractValues(inputArray); - this.outputObject = this.convert(flattenedArray, mappingDefinitions); + this.outputObject = this.convert(flattenedArray, mappingDefinitions, keepUngroupedContents); return this; } @@ -45,7 +45,7 @@ export class SparqlResultConverter { * @param {*} inputArray An array representing data structured as a table * @param {*} mappingDefinitions An array of MappingDefinition objects representing the structure of the final output */ - private convert(inputArray: Record[], mappingDefinitions: Partial[]): Record> { + private convert(inputArray: Record[], mappingDefinitions: Partial[], keepUngroupedContents: boolean): Record> { const outputObject = {}; // Loop over mapping definitions, there could be multiple definitions on one layer @@ -57,7 +57,7 @@ export class SparqlResultConverter { // Create a new array and add it under "rootName" outputObject[mappingDefinition.rootName] = new Array(); - // Group the inputArray and extract all grouped keys from the contained elements + // Group the inputArray const groupedObject = this.groupArray(inputArray, mappingDefinition); // After grouping the array, we have to check for every element of the groupedObject if it can be further grouped @@ -77,7 +77,7 @@ export class SparqlResultConverter { if (mappingDefinition.childMappings){ // If this mapDef has childMappings -> call convert() recursively on the groupedElement - groupedArrayToPush = this.convert(groupedElement as Record[], mappingDefinition.childMappings); + groupedArrayToPush = this.convert(groupedElement as Record[], mappingDefinition.childMappings, keepUngroupedContents); } else { // if there are no more childMappings -> create a copy of the groupedElement and remove all elements that might be grouped in the next steps // (i.e. elements with key = propertyToGroup). All the remaining bits (stuff that doesn't get grouped) is added under "children" @@ -89,7 +89,7 @@ export class SparqlResultConverter { delete elem[keysToDelete]; }); }); - if(!clonedGroupedElement.every(elem => isEmpty(elem))) { + if(keepUngroupedContents && !clonedGroupedElement.every(elem => isEmpty(elem))) { groupedArrayToPush = {children: [ ...clonedGroupedElement]}; } @@ -107,7 +107,7 @@ export class SparqlResultConverter { } } else { // if there is nothing to group and the inputArry is not empty -> Simple return the inputArray as output - if(!inputArray.every(elem => isEmpty(elem))) { + if(!inputArray.every(elem => isEmpty(elem)) && keepUngroupedContents) { outputObject[mappingDefinition.rootName] = [...inputArray]; } } From 7f4b89ccec8f1a184399384774be717d937dfa8f Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:32:12 +0200 Subject: [PATCH 6/9] Minor renaming to clear test target --- tests/multiplePropertyTests/testConverting.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/multiplePropertyTests/testConverting.test.ts b/tests/multiplePropertyTests/testConverting.test.ts index 5562050..5522c85 100644 --- a/tests/multiplePropertyTests/testConverting.test.ts +++ b/tests/multiplePropertyTests/testConverting.test.ts @@ -8,7 +8,7 @@ import {oneLayerTestData, twoLayerTestData} from './test-data'; const resultConverter = new SparqlResultConverter(); -describe('One-Layer-Test with multiple properties on first layer', () => { +describe('Tests including multiple properties on one layer', () => { it('Should group a result on two layers respecting multiple properties on the first layer', () => { // Object that defines the structure of the result const oneLayerMappingDefinition: MappingDefinition[] = [ @@ -32,11 +32,9 @@ describe('One-Layer-Test with multiple properties on first layer', () => { const convertedResult = resultConverter.convertToDefinition(oneLayerTestData.results, oneLayerMappingDefinition).getAll(); assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion failed...'); }); -}); -describe('Two-Layer-Test with multiple properties on the second layer', () => { it('Should group a result on two layers respecting multiple properties on the second layer', () => { const twoLayerMappingDefinition: MappingDefinition[] = [ From b4dad3e4c8b58f80c228a38ffba65162add17b4e Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:32:20 +0200 Subject: [PATCH 7/9] Added a more complex test --- tests/skillTests/expectedResults.ts | 35 ++++ tests/skillTests/skill-tests.test.ts | 53 ++++++ tests/skillTests/test-data.ts | 240 +++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 tests/skillTests/expectedResults.ts create mode 100644 tests/skillTests/skill-tests.test.ts create mode 100644 tests/skillTests/test-data.ts diff --git a/tests/skillTests/expectedResults.ts b/tests/skillTests/expectedResults.ts new file mode 100644 index 0000000..528ec8c --- /dev/null +++ b/tests/skillTests/expectedResults.ts @@ -0,0 +1,35 @@ +// Expected result for a very simple one layer mapping including non-grouped children elements: +export const skillResult = { + skills : [ + { + skillIri: 'https://siemens.de/skills#OpcUaSkill', + stateMachine: 'https://siemens.de/skills#OpcUaSkill_StateMachine', + currentStateTypeIri: 'http://www.hsu-ifa.de/ontologies/ISA-TR88#Idle', + skillParameters: [ + { + parameterIri: 'https://siemens.de/skills#OpcUaSkill_a', + parameterName: 'a', + parameterType: 'Integer', + parameterRequired: 'true', + parameterDefault: '0', + parameterOptionValues: [ { value: '2' }, { value: '3' }, { value: '4' } ] + }, + { + parameterIri: 'https://siemens.de/skills#OpcUaSkill_b', + parameterName: 'b', + parameterType: 'Integer', + parameterRequired: 'true', + parameterDefault: '0', + } + ], + skillOutputs: [ + { + outputIri: 'https://siemens.de/skills#OpcUaSkill_result', + outputName: 'result', + outputType: 'Integer', + outputRequired: 'false' + } + ] + } + ] +}; diff --git a/tests/skillTests/skill-tests.test.ts b/tests/skillTests/skill-tests.test.ts new file mode 100644 index 0000000..76bc0bd --- /dev/null +++ b/tests/skillTests/skill-tests.test.ts @@ -0,0 +1,53 @@ +import { assert } from 'chai'; +import { skillResult } from './expectedResults'; + +import { SparqlResultConverter, MappingDefinition } from "../../src/SparqlResultConverter"; + +import {testData} from './test-data'; + +const resultConverter = new SparqlResultConverter(); + +describe('Tests for complex skills', () => { + it('Should group a skill according to definition', () => { + + const skillMapping: MappingDefinition[] = [ + { + rootName: 'skills', + propertyToGroup: 'skill', + name: 'skillIri', + toCollect: ['stateMachine', 'currentStateTypeIri'], + childMappings: [ + { + rootName: 'skillParameters', + propertyToGroup: 'parameterIri', + name: 'parameterIri', + toCollect: ['parameterIri', 'parameterName', 'parameterType', 'parameterRequired', 'parameterDefault'], + childMappings: [ + { + rootName: 'parameterOptionValues', + propertyToGroup: 'paramOptionValue', + name: 'value', + } + ] + }, + { + rootName: 'skillOutputs', + propertyToGroup: 'outputIri', + name: 'outputIri', + toCollect: ['outputIri', 'outputName', 'outputType', 'outputRequired', 'outputDefault'], + childMappings: [ + { + rootName: 'outputOptionValues', + propertyToGroup: 'outputOptionValue', + name: 'value', + } + ] + }, + ] + }, + ]; + const convertedResult = resultConverter.convertToDefinition(testData.results.bindings, skillMapping, false).getAll(); + + assert.deepEqual(convertedResult, skillResult, 'Testing two layer conversion without property collection failed...'); + }); +}); diff --git a/tests/skillTests/test-data.ts b/tests/skillTests/test-data.ts new file mode 100644 index 0000000..133ab4c --- /dev/null +++ b/tests/skillTests/test-data.ts @@ -0,0 +1,240 @@ +/** + * Mock-up of a piece of data that could be returned from a DB-Query. + */ +export const testData = { + "head": { + "vars": [ + "skill", + "stateMachine", + "currentStateTypeIri", + "parameterIri", + "parameterName", + "parameterType", + "parameterRequired", + "parameterDefault", + "paramOptionValue", + "outputIri", + "outputName", + "outputType", + "outputRequired", + "outputDefault", + "outputOptionValue" + ] + }, + "results": { + "bindings": [ + { + "parameterType": { + "type": "literal", + "value": "Integer" + }, + "outputName": { + "type": "literal", + "value": "result" + }, + "currentStateTypeIri": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/ISA-TR88#Idle" + }, + "outputType": { + "type": "literal", + "value": "Integer" + }, + "paramOptionValue": { + "type": "literal", + "value": "4" + }, + "parameterName": { + "type": "literal", + "value": "a" + }, + "parameterDefault": { + "type": "literal", + "value": "0" + }, + "outputIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_result" + }, + "stateMachine": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_StateMachine" + }, + "skill": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill" + }, + "outputRequired": { + "type": "literal", + "value": "false" + }, + "parameterIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_a" + }, + "parameterRequired": { + "type": "literal", + "value": "true" + } + }, + { + "parameterType": { + "type": "literal", + "value": "Integer" + }, + "outputName": { + "type": "literal", + "value": "result" + }, + "currentStateTypeIri": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/ISA-TR88#Idle" + }, + "outputType": { + "type": "literal", + "value": "Integer" + }, + "paramOptionValue": { + "type": "literal", + "value": "3" + }, + "parameterName": { + "type": "literal", + "value": "a" + }, + "parameterDefault": { + "type": "literal", + "value": "0" + }, + "outputIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_result" + }, + "stateMachine": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_StateMachine" + }, + "skill": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill" + }, + "outputRequired": { + "type": "literal", + "value": "false" + }, + "parameterIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_a" + }, + "parameterRequired": { + "type": "literal", + "value": "true" + } + }, + { + "parameterType": { + "type": "literal", + "value": "Integer" + }, + "outputName": { + "type": "literal", + "value": "result" + }, + "currentStateTypeIri": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/ISA-TR88#Idle" + }, + "outputType": { + "type": "literal", + "value": "Integer" + }, + "paramOptionValue": { + "type": "literal", + "value": "2" + }, + "parameterName": { + "type": "literal", + "value": "a" + }, + "parameterDefault": { + "type": "literal", + "value": "0" + }, + "outputIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_result" + }, + "stateMachine": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_StateMachine" + }, + "skill": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill" + }, + "outputRequired": { + "type": "literal", + "value": "false" + }, + "parameterIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_a" + }, + "parameterRequired": { + "type": "literal", + "value": "true" + } + }, + { + "parameterType": { + "type": "literal", + "value": "Integer" + }, + "outputIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_result" + }, + "stateMachine": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_StateMachine" + }, + "outputName": { + "type": "literal", + "value": "result" + }, + "currentStateTypeIri": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/ISA-TR88#Idle" + }, + "skill": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill" + }, + "outputRequired": { + "type": "literal", + "value": "false" + }, + "parameterIri": { + "type": "uri", + "value": "https://siemens.de/skills#OpcUaSkill_b" + }, + "outputType": { + "type": "literal", + "value": "Integer" + }, + "parameterName": { + "type": "literal", + "value": "b" + }, + "parameterRequired": { + "type": "literal", + "value": "true" + }, + "parameterDefault": { + "type": "literal", + "value": "0" + } + } + ] + } +}; From 97321d6420aa1680f20ac1173804ef4db568731a Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:32:33 +0200 Subject: [PATCH 8/9] Set version to 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f65cc8f..11123c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sparql-result-converter", - "version": "2.0.2", + "version": "3.0.0", "description": "Little utility function to that converts the table-like result of a SPARQL query into a JSON tree with a user-defined structure", "scripts": { "clean": "rimraf dist && rimraf *.tsbuildinfo", From ab3ae49f49a7d1440e07a5928b7ca15799204bfb Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sun, 6 Sep 2020 13:36:08 +0200 Subject: [PATCH 9/9] Fixed line length --- src/SparqlResultConverter.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts index bb1f2e1..893d3bf 100644 --- a/src/SparqlResultConverter.ts +++ b/src/SparqlResultConverter.ts @@ -33,7 +33,8 @@ export class SparqlResultConverter { // return; // } - public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[], keepUngroupedContents = true): SparqlResultConverter { + public convertToDefinition(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[], keepUngroupedContents = true) + : SparqlResultConverter { const flattenedArray = ArrayUtil.extractValues(inputArray); this.outputObject = this.convert(flattenedArray, mappingDefinitions, keepUngroupedContents); return this; @@ -45,7 +46,8 @@ export class SparqlResultConverter { * @param {*} inputArray An array representing data structured as a table * @param {*} mappingDefinitions An array of MappingDefinition objects representing the structure of the final output */ - private convert(inputArray: Record[], mappingDefinitions: Partial[], keepUngroupedContents: boolean): Record> { + private convert(inputArray: Record[], mappingDefinitions: Partial[], keepUngroupedContents: boolean) + : Record> { const outputObject = {}; // Loop over mapping definitions, there could be multiple definitions on one layer @@ -62,7 +64,7 @@ export class SparqlResultConverter { // After grouping the array, we have to check for every element of the groupedObject if it can be further grouped for (const key in groupedObject) { - const groupedElement = groupedObject[key]; + const groupedElement = groupedObject[key] as Record[]; // toCollect can be used to specify common properties of the superordinate element -> Here we extract these properties // TODO: Not only take groupedElement[0], but make sure the properties to collect are equal for all groupedElements @@ -77,7 +79,7 @@ export class SparqlResultConverter { if (mappingDefinition.childMappings){ // If this mapDef has childMappings -> call convert() recursively on the groupedElement - groupedArrayToPush = this.convert(groupedElement as Record[], mappingDefinition.childMappings, keepUngroupedContents); + groupedArrayToPush = this.convert(groupedElement, mappingDefinition.childMappings, keepUngroupedContents); } else { // if there are no more childMappings -> create a copy of the groupedElement and remove all elements that might be grouped in the next steps // (i.e. elements with key = propertyToGroup). All the remaining bits (stuff that doesn't get grouped) is added under "children"