diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ea9d8fd..0000000 --- a/.babelrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "plugins": ["transform-object-rest-spread"], - "env": { - "development": { - "presets": ["env"], - "plugins": [ - "add-module-exports" - ] - }, - "production": { - "presets": ["env", "minify"], - "plugins": [ - "add-module-exports" - ] - } - } -} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..1c7e978 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +!**/.eslintrc* +node_modules* +dist +*.svg +*.ico +*.json +.gitignore +*.md +*.log +*.lock diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index a5e9d63..0000000 --- a/.eslintrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parser": "babel-eslint", - "extends": "airbnb", - "env": { - "mocha": true - }, - "rules": { - "comma-dangle": ["error", "only-multiline"], - "no-plusplus": "off", - "no-return-assign": ["error", "except-parens"], - "max-len": [2, 120, 2, {"ignoreComments": true}], - "prefer-destructuring": "off", - "no-param-reassign": "off" - } - } diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..864f46a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,23 @@ + +/* eslint-disable no-undef */ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + "eslint:recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + }, + rules: { + "semi": ["error","always"], + "indent": ["error", 4], + "prefer-const": ["error",{}], + "max-len": ["error", {code: 140, ignoreComments: true}] + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + }, +}; diff --git a/.gitignore b/.gitignore index 02c7823..cda8e80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,12 @@ +# compiled output +/dist +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out +tsconfig.build.tsbuildinfo +tsconfig.tsbuildinfo + # Logs logs *.log @@ -41,4 +50,4 @@ package-lock.json yarn.lock others -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..b16c68d --- /dev/null +++ b/.nycrc @@ -0,0 +1,27 @@ +{ + "cache": false, + "check-coverage": false, + "extension": [ + ".ts" + ], + "include": [ + "**/*.js", + "**/*.ts" + ], + "exclude": [ + "coverage/**", + "node_modules/**", + "**/*.d.ts", + "**/*.test.ts", + "tests/*", + ".eslintrc.js" + ], + "sourceMap": true, + "reporter": [ + "html", + "text", + "text-summary" + ], + "all": true, + "instrument": true +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..867b969 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,42 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Mocha Current File", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "--no-timeouts", + "--colors", + "${file}", + "--require", + "ts-node/register" + ], + "console": "integratedTerminal", + "sourceMaps": true, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Attach", + "port": 9229, + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "pwa-node" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\lib\\sparql-result-converter.js" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e7f99cb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "eslint.workingDirectories": [ + { "mode": "auto" } +], +} diff --git a/README.md b/README.md index c299098..16ffed3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ +![npm](https://img.shields.io/npm/v/sparql-result-converter) +![GitHub](https://img.shields.io/github/license/aljoshakoecher/sparql-result-converter) + # sparql-result-converter A little utility class that helps you to transform the table-like results that you get from a SPARQL query into a nested JSON tree with a user-defined structure. -# How to use it +## How to use it Just install as a dependecy via `npm install sparql-result-converter`. In your code you can then use the converter like this: ```javascript @@ -10,11 +13,11 @@ const SparqlResultConverter = require('sparql-result-converter'); const converter = new SparqlResultConverter(); // convert a SPARQL result -const convertedResult = converter.convert(sparqlResult, convertStructure); +const convertedResult = converter.convert(inputArray, mappingDefinitions); ``` You have to pass two parameters to `convert()`: -1) `sparqlResult` are the results you get from your SPARQL query -2) `convertStructure` is an array that describes how your converted result should look like. It looks like this: +1) `inputArray` are the results you get from your SPARQL query +2) `mappingDefinitions` is an array that describes how your converted result should look like. It consists of mapping definitions looking like this: ```javascript const convertStructure = [ @@ -34,7 +37,7 @@ When you query Triple stores with their REST API, you get a tabular structure wh ![Example Graph](https://github.com/aljoshakoecher/sparql-result-converter/raw/documentation/images/docu-images/example-graph.png) -There are three pet owners with varying numbers of pets. While Peter has only one pet, mary has three. If we'd like to find all pet owners, their pets and the type of pet(s) they have, we migh send the following query against our triple store: +There are three pet owners with varying numbers of pets. While Peter has only one pet, mary has three. If we'd like to find all pet owners, their pets and the type of pet(s) they have, we might send the following query against our triple store: ```SPARQL PREFIX ex: PREFIX rdf: diff --git a/package.json b/package.json index c6ad005..7a199c7 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,20 @@ { "name": "sparql-result-converter", - "version": "1.0.2", + "version": "1.0.3", "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 lib", - "test": "npm run lint && npm run cover", - "test:prod": "cross-env BABEL_ENV=production npm run test", - "test:only": "mocha --require babel-register --require babel-core/register --require babel-polyfill --recursive", - "test:watch": "npm test -- --watch", - "cover": "nyc --check-coverage npm run test:only", - "lint": "eslint src test", - "build": "cross-env BABEL_ENV=production babel src --out-dir lib", - "prepare": "npm run clean && npm run build &&npm run lint && npm run test" + "clean": "rimraf dist", + "build": "tsc --project tsconfig.build.json", + "test": "npm run lint && npm run coverage", + "test:only": "mocha -r ts-node/register tests/**/*.test.ts", + "coverage": "nyc npm run test:only", + "lint": "eslint src tests/**/* --ext .ts", + "prepublishOnly": "npm run lint && npm run coverage && npm run build" }, "files": [ - "lib", - "src" + "dist/**/*" ], - "main": "./lib/sparql-result-converter.js", + "main": "./dist/SparqlResultConverter.js", "repository": { "type": "git", "url": "git+https://github.com/aljoshakoecher/sparql-result-converter.git" @@ -38,26 +35,22 @@ }, "homepage": "https://github.com/aljoshakoecher/sparql-result-converter#readme", "dependencies": { - "json-groupby": "^1.1.0", - "lodash": "^4.17.15" + "lodash.groupby": "^4.6.0", + "lodash.isempty": "^4.4.0" }, "devDependencies": { - "babel-cli": "^6.26.0", - "babel-eslint": "^10.0.1", - "babel-plugin-add-module-exports": "^1.0.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-polyfill": "^6.26.0", - "babel-preset-env": "^1.6.1", - "babel-preset-minify": "0.3.0", + "@types/chai": "^4.2.11", + "@types/lodash.isempty": "^4.4.6", + "@types/mocha": "^7.0.2", + "@typescript-eslint/eslint-plugin": "^3.3.0", + "@typescript-eslint/parser": "^3.3.0", "chai": "^4.1.2", - "cross-env": "^5.1.3", - "eslint": "^5.16.0", - "eslint-config-airbnb": "^17.1.0", + "eslint": "^6.8.0", "eslint-plugin-import": "^2.7.0", - "eslint-plugin-jsx-a11y": "^6.0.2", - "eslint-plugin-react": "^7.4.0", "mocha": "^6.1.3", - "nyc": "^13.3.0", - "rimraf": "^2.6.2" + "nyc": "^15.1.0", + "rimraf": "^2.6.2", + "ts-node": "^8.10.2", + "typescript": "^3.9.5" } } diff --git a/src/ArrayUtil.ts b/src/ArrayUtil.ts new file mode 100644 index 0000000..4e88bd1 --- /dev/null +++ b/src/ArrayUtil.ts @@ -0,0 +1,55 @@ +export class ArrayUtil { + + /** + * Checks whether or not all entries of the array contain the property that is used for grouping + * @param {*} arrayToCheck + * @param {*} groupingProperty + */ + static allEntriesContainGroupingProperty(arrayToCheck: any[], groupingProperty: string): boolean { + for (let i = 0; i < arrayToCheck.length; i++) { + const element = arrayToCheck[i]; + if (!Object.prototype.hasOwnProperty.call(element, groupingProperty)) { + return false; + } + } + return true; + } + + + + /** + * Transforms an array of objects to an array of simple datatypes by extracting every value-property. + * @param {*} sparqlResult Array of objects that contain a value-property + */ + static extractValues(sparqlResult: SparqlResultLine[]): TransformedSparqlResultElement[] { + + const outputArray = new Array(); + + // Take every array element and extract all values of all object keys -> flatten the array + sparqlResult.forEach((sparqlResultLine:SparqlResultLine) => { + const objectKeys = Object.keys(sparqlResultLine); + + const outputElement = {}; + objectKeys.forEach((key) => { + outputElement[key] = sparqlResultLine[key].value; + }); + outputArray.push(outputElement); + }); + + return outputArray; + } + + +} + +export interface SparqlResultLine { + [name: string]: { + type: string, + value: string + } +} + +export interface TransformedSparqlResultElement { + [propName: string]: string; +} + diff --git a/src/SparqlResultConverter.ts b/src/SparqlResultConverter.ts new file mode 100644 index 0000000..8cd6c00 --- /dev/null +++ b/src/SparqlResultConverter.ts @@ -0,0 +1,98 @@ +import { groupBy } from "lodash"; +import { isEmpty } from "lodash"; +import { ArrayUtil, SparqlResultLine } from "./ArrayUtil"; + +// Maps the query result of "select_allModules" to an array of Modules +export class SparqlResultConverter { + /** + * Groups a table-structure and converts it to a tree-like structure + * @param {*} inputArray An array representing data structured as a table + * @param {*} mappingDefinitions An array of objects representing the structure of the final output + */ + convert(inputArray: SparqlResultLine[], mappingDefinitions: MappingDefinition[], currElement = 0): unknown[] { + let flattenedArray; + + // first: transform array + if (currElement === 0) { + flattenedArray = ArrayUtil.extractValues(inputArray); + } else { + flattenedArray = inputArray; + } + + // get the current mapping object + const currentMappingDefition = mappingDefinitions[currElement]; + + // group the flattened array by the current mapping object's "objectToGroup" property + const groupedObject = groupBy(flattenedArray, (elem) => elem[currentMappingDefition.objectToGroup]); + + // Empty the outputArray array, it will later be filled with the grouped content + const outputArray = []; + + Object.keys(groupedObject).forEach((key) => { + let groupedElement = groupedObject[key]; + + // Collect all elements that should be collected + // TODO: Not only take groupedElement[0], but make sure the properties to collect are equal for all groupedElements + const elemsToCollect = {}; + if(currentMappingDefition.toCollect) { + currentMappingDefition.toCollect.forEach((elemToCollect) => { + elemsToCollect[elemToCollect] = groupedElement[0][elemToCollect]; + groupedElement.forEach((inputElem) => { + delete inputElem[elemToCollect]; + }); + }); + } + + + if (currElement <= (mappingDefinitions.length - 2)) { + if (ArrayUtil.allEntriesContainGroupingProperty(groupedElement, mappingDefinitions[currElement + 1].objectToGroup)) { + groupedElement = (this.convert(groupedElement, mappingDefinitions, currElement + 1)); + } + } + + + // Delete the all elements that have already been grouped + groupedElement.forEach((element) => { + mappingDefinitions.forEach((mapDef) => { + delete element[mapDef.objectToGroup]; + }); + }); + + + const nameToPush = { + [currentMappingDefition.name]: key, + }; + + let objToPush = {}; + if (!isEmpty(groupedElement[0])) { + const groupToPush = { + [currentMappingDefition.childRoot]: groupedElement + }; + objToPush = { + ...nameToPush, + ...elemsToCollect, + ...groupToPush + }; + } else { + objToPush = { + ...nameToPush, + ...elemsToCollect + }; + } + + + // Add the grouped element to the outputArray + outputArray.push(objToPush); + }); + + return outputArray; + } +} + + +interface MappingDefinition { + objectToGroup: string, + name: string, + childRoot: string, + toCollect?: string[]; +} diff --git a/src/array-util.js b/src/array-util.js deleted file mode 100644 index d22bcd7..0000000 --- a/src/array-util.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Checks whether or not all entries of the array contain the property that is used for grouping - * @param {*} arrayToCheck - * @param {*} groupingProperty - */ -function allEntriesContainGroupingProperty(arrayToCheck, groupingProperty) { - for (let i = 0; i < arrayToCheck.length; i++) { - const element = arrayToCheck[i]; - if (!Object.prototype.hasOwnProperty.call(element, groupingProperty)) { - return false; - } - } - return true; -} - -/** - * Transforms an array of objects to an array of simple datatypes by extracting every value-property. - * @param {*} inputArray Array of objects that contain a value-property - */ -function transformArray(inputArray) { - const transformedArray = inputArray; - - // Take every array element and extract all values of all object keys -> flatten the array - transformedArray.forEach((arrayObject) => { - const objectKeys = Object.keys(arrayObject); - - objectKeys.forEach((key) => { - arrayObject[key] = arrayObject[key].value; - }); - }); - - return transformedArray; -} - - -module.exports = { - allEntriesContainGroupingProperty, - transformArray -}; diff --git a/src/sparql-result-converter.js b/src/sparql-result-converter.js deleted file mode 100644 index 344d905..0000000 --- a/src/sparql-result-converter.js +++ /dev/null @@ -1,95 +0,0 @@ -const groupBy = require('json-groupby'); -const _ = require('lodash'); - -const transformArray = require('./array-util').transformArray; -const allEntriesContainGroupingProperty = require('./array-util').allEntriesContainGroupingProperty; - -// Maps the query result of "select_allModules" to an array of Modules -class SparqlResultConverter { - /** - * Groups a table-structure and converts it to a tree-like structure - * @param {*} inputArray An array representing data structured as a table - * @param {*} treeModel An object representing the final output (as a tree-structure) - */ - convert(inputArray, treeModel, currElement = 0) { - let outputArray; - - // first: transform array - if (currElement === 0) { - outputArray = transformArray(inputArray); - } - - - // get currrent element and fix child root - const currGroup = treeModel[currElement]; - currGroup.childRoot = typeof currGroup.childRoot === 'undefined' ? 'content' : currGroup.childRoot; - - // group the ungrouped outputArray - const groupedArray = groupBy(outputArray, [currGroup.objectToGroup]); - - // Empty the outputArray array, it will later be filled with the grouped content - outputArray = []; - - Object.keys(groupedArray).forEach((key) => { - let groupedElement = groupedArray[key]; - - // Collect all elements that should be collected - // TODO: Not only take groupedElement[0], but make sure the properties to collect are equal for all groupedElements - const elemsToCollect = {}; - if (Object.prototype.hasOwnProperty.call(currGroup, 'toCollect')) { - currGroup.toCollect.forEach((elemToCollect) => { - elemsToCollect[elemToCollect] = groupedElement[0][elemToCollect]; - groupedElement.forEach((inputElem) => { - delete inputElem[elemToCollect]; - }); - }); - } - - - if (currElement <= (treeModel.length - 2)) { - if (allEntriesContainGroupingProperty(groupedElement, treeModel[currElement + 1].objectToGroup)) { - groupedElement = (this.convert(groupedElement, treeModel, currElement + 1)); - } - } - - - // Delete the all elements that have already been grouped - groupedElement.forEach((element) => { - treeModel.forEach((group) => { - delete element[group.objectToGroup]; - }); - }); - - - const nameToPush = { - [currGroup.name]: key, - }; - - let objToPush = {}; - if (!_.isEmpty(groupedElement[0])) { - const groupToPush = { - [currGroup.childRoot]: groupedElement - }; - objToPush = { - ...nameToPush, - ...elemsToCollect, - ...groupToPush - }; - } else { - objToPush = { - ...nameToPush, - ...elemsToCollect - }; - } - - - // Add the grouped element to the inputArray - outputArray.push(objToPush); - }); - - return outputArray; - } -} - - -module.exports = SparqlResultConverter; diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 6cf73bc..0000000 --- a/test/index.js +++ /dev/null @@ -1,66 +0,0 @@ -import { assert } from 'chai'; - -const SparqlResultConverter = require('../lib/sparql-result-converter'); -const testData = require('./test-data'); - -const resultConverter = new SparqlResultConverter(); - - -describe('The test', () => { - it('should test table-to-tree-conversion', () => { - // Object that defines the structure of the result - const resultObject = [ - { - objectToGroup: 'owner', - name: 'ownerName', - childRoot: 'pets' - } - ]; - - // Expected result: - const expectedResult = [ - { - ownerName: 'Peter', - pets: [ - { - petName: 'Rex', - petType: 'Dog' - } - ] - }, - { - ownerName: 'John', - pets: [ - { - petName: 'Lassie', - petType: 'Dog' - }, - { - petName: 'Oliver', - petType: 'Cat' - } - ] - }, - { - ownerName: 'Mary', - pets: [ - { - petName: 'Huey', - petType: 'Cat' - }, - { - petName: 'Dewey', - petType: 'Cat' - }, - { - petName: 'Louie', - petType: 'Cat' - } - ] - } - ]; - - const convertedResult = resultConverter.convert(testData.results, resultObject); - assert.deepEqual(convertedResult, expectedResult, 'Testing table-to-tree-conversion failed...'); - }); -}); diff --git a/test/test-data.js b/test/test-data.js deleted file mode 100644 index 5f15629..0000000 --- a/test/test-data.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Mock-up of a piece of data that could be returned from a DB-Query - */ -const testData = { - head: ['owner', 'petName', 'petType'], - results: [ - { - owner: { - type: 'String', - value: 'Peter', - }, - petName: { - type: 'String', - value: 'Rex' - }, - petType: { - type: 'String', - value: 'Dog' - } - }, - { - owner: { - type: 'String', - value: 'John', - }, - petName: { - type: 'String', - value: 'Lassie' - }, - petType: { - type: 'String', - value: 'Dog' - } - }, - { - owner: { - type: 'String', - value: 'John', - }, - petName: { - type: 'String', - value: 'Oliver' - }, - petType: { - type: 'String', - value: 'Cat' - } - }, - { - owner: { - type: 'String', - value: 'Mary', - }, - petName: { - type: 'String', - value: 'Huey' - }, - petType: { - type: 'String', - value: 'Cat' - } - }, - { - owner: { - type: 'String', - value: 'Mary', - }, - petName: { - type: 'String', - value: 'Dewey' - }, - petType: { - type: 'String', - value: 'Cat' - } - }, - { - owner: { - type: 'String', - value: 'Mary', - }, - petName: { - type: 'String', - value: 'Louie' - }, - petType: { - type: 'String', - value: 'Cat' - } - }, - ] -}; - -export default testData; diff --git a/tests/testArrayUtil/ArrayUtil.test.ts b/tests/testArrayUtil/ArrayUtil.test.ts new file mode 100644 index 0000000..92cf6ea --- /dev/null +++ b/tests/testArrayUtil/ArrayUtil.test.ts @@ -0,0 +1,94 @@ +import { assert } from 'chai'; +import { ArrayUtil, SparqlResultLine } from '../../src/ArrayUtil'; + + +describe('ArrayUtil Test', () => { + it('Should extract value property of each entry', () => { + // Object that defines the structure of the result + const input: SparqlResultLine[] = + [ + { + "capability": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/capability-example#FraesenCapability" + }, + "skill": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/capability-example#FraesenSkill" + } + }, + { + "capability": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/capability-example#FraesenCapability" + }, + "skill": { + "type": "uri", + "value": "http://www.hsu-ifa.de/ontologies/capability-example#FraesenSkill_REST" + } + } + ]; + + const expectedResult = [ + { + capability: "http://www.hsu-ifa.de/ontologies/capability-example#FraesenCapability", + skill: "http://www.hsu-ifa.de/ontologies/capability-example#FraesenSkill" + }, + { + capability: "http://www.hsu-ifa.de/ontologies/capability-example#FraesenCapability", + skill: "http://www.hsu-ifa.de/ontologies/capability-example#FraesenSkill_REST" + } + ]; + + + const transformedArray = ArrayUtil.extractValues(input); + assert.deepEqual(transformedArray, expectedResult, 'Expected result should have all values extracted...'); + }); + + + it('Should confirm that all array entries contain grouping property', () => { + // Just some random array + const input = + [ + { + name: "Peter", + age: 55 + }, + { + name: "John", + age: 43 + }, + { + name: "Mary", + age: 29 + } + ]; + + const result = ArrayUtil.allEntriesContainGroupingProperty(input, "name"); + assert.isTrue(result, 'Should return true because all array entries contain grouping property...'); + }); + + + it('Should confirm that not all array entries contain grouping property', () => { + // Just some random array + const input = + [ + { + name: "Peter", + age: 55, + city: "Boston" + }, + { + name: "John", + age: 43, + city: "New York City" + }, + { + name: "Mary", + age: 29 + } + ]; + const result = ArrayUtil.allEntriesContainGroupingProperty(input, "city"); + assert.isFalse(result, 'Should return false because not all array entries contain grouping property...'); + }); +}); diff --git a/tests/testConverting/expectedResults.ts b/tests/testConverting/expectedResults.ts new file mode 100644 index 0000000..53c4986 --- /dev/null +++ b/tests/testConverting/expectedResults.ts @@ -0,0 +1,86 @@ +// Expected result for one layer mapping: +export const expectedOneLayerResult = [ + { + ownerName: 'Peter', + pets: [ + { + petName: 'Rex', + petType: 'Dog' + } + ] + }, + { + ownerName: 'John', + pets: [ + { + petName: 'Lassie', + petType: 'Dog' + }, + { + petName: 'Oliver', + petType: 'Cat' + } + ] + }, + { + ownerName: 'Mary', + pets: [ + { + petName: 'Huey', + petType: 'Cat' + }, + { + petName: 'Dewey', + petType: 'Cat' + }, + { + petName: 'Louie', + petType: 'Cat' + } + ] + } +]; + +export const expectedTwoLayerResult = [ + { + ownerName: 'Peter', + petTypes: [ + { + type: 'Dog', + pets: [ + { petName: 'Rex' } + ] + } + ] + }, + { + ownerName: 'John', + petTypes: [ + { + type: 'Dog', + pets: [ + { petName: 'Lassie' } + ] + }, + { + type: 'Cat', + pets: [ + { petName: 'Oliver' } + ] + } + ] + }, + { + ownerName: 'Mary', + petTypes: [ + { + type: 'Cat', + pets: [ + { petName: 'Huey' }, + { petName: 'Dewey' }, + { petName: 'Louie' }, + ] + } + ] + } +]; diff --git a/tests/testConverting/test-data.ts b/tests/testConverting/test-data.ts new file mode 100644 index 0000000..78f45a6 --- /dev/null +++ b/tests/testConverting/test-data.ts @@ -0,0 +1,92 @@ +/** + * Mock-up of a piece of data that could be returned from a DB-Query + */ +export const testData = { + head: ['owner', 'petName', 'petType'], + results: [ + { + owner: { + type: 'String', + value: 'Peter', + }, + petName: { + type: 'String', + value: 'Rex' + }, + petType: { + type: 'String', + value: 'Dog' + } + }, + { + owner: { + type: 'String', + value: 'John', + }, + petName: { + type: 'String', + value: 'Lassie' + }, + petType: { + type: 'String', + value: 'Dog' + } + }, + { + owner: { + type: 'String', + value: 'John', + }, + petName: { + type: 'String', + value: 'Oliver' + }, + petType: { + type: 'String', + value: 'Cat' + } + }, + { + owner: { + type: 'String', + value: 'Mary', + }, + petName: { + type: 'String', + value: 'Huey' + }, + petType: { + type: 'String', + value: 'Cat' + } + }, + { + owner: { + type: 'String', + value: 'Mary', + }, + petName: { + type: 'String', + value: 'Dewey' + }, + petType: { + type: 'String', + value: 'Cat' + } + }, + { + owner: { + type: 'String', + value: 'Mary', + }, + petName: { + type: 'String', + value: 'Louie' + }, + petType: { + type: 'String', + value: 'Cat' + } + }, + ] +}; diff --git a/tests/testConverting/testConverting.test.ts b/tests/testConverting/testConverting.test.ts new file mode 100644 index 0000000..0ceb7f3 --- /dev/null +++ b/tests/testConverting/testConverting.test.ts @@ -0,0 +1,46 @@ +import { assert } from 'chai'; +import { expectedOneLayerResult, expectedTwoLayerResult } from './expectedResults'; + +import { SparqlResultConverter } from "../../src/SparqlResultConverter"; + +import {testData} from './test-data'; + +const resultConverter = new SparqlResultConverter(); + + +describe('One Layer Test', () => { + it('Should group a result on one layer', () => { + // Object that defines the structure of the result + const oneLayerMappingDefinition = [ + { + objectToGroup: 'owner', + name: 'ownerName', + childRoot: 'pets' + } + ]; + + const convertedResult = resultConverter.convert(testData.results, oneLayerMappingDefinition); + assert.deepEqual(convertedResult, expectedOneLayerResult, 'Testing one layer conversion failed...'); + }); +}); + +describe('Two Layer Test', () => { + it('Should group a result on two layers', () => { + // Object that defines the structure of the result + const twoLayerMappingDefinition = [ + { + objectToGroup: 'owner', + name: 'ownerName', + childRoot: 'petTypes' + }, + { + objectToGroup: 'petType', + name: 'type', + childRoot: 'pets' + } + ]; + const convertedResult = resultConverter.convert(testData.results, twoLayerMappingDefinition); + + assert.deepEqual(convertedResult, expectedTwoLayerResult, 'Testing two layer conversion failed...'); + }); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..4eade54 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "tests", "**/*test.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..aaa944e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + /* Truncated compiler options to list only relevant options */ + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "composite": true, + "rootDir": "./src", + "baseUrl": "./", + "module": "UMD", + "target": "es2017", + }, + "exclude": ["dist", "node_modules"] + }