Commit 3e9b725c authored by Marcel Nellesen's avatar Marcel Nellesen
Browse files

Merge branch 'Product/1210-inheritance' into 'master'

Product/1210 inheritance

See merge request !50
parents 37b6b724 47d975b0
{
"name": "@coscine/form-generator",
"version": "1.10.1",
"version": "1.10.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -3605,9 +3605,9 @@
}
},
"@types/node": {
"version": "13.13.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz",
"integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A=="
"version": "14.14.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz",
"integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g=="
},
"@types/normalize-package-data": {
"version": "2.4.0",
......@@ -3653,9 +3653,9 @@
}
},
"@types/rdfjs__namespace": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@types/rdfjs__namespace/-/rdfjs__namespace-1.1.2.tgz",
"integrity": "sha512-xgRo4FkwZrm8EZN/z1eG9FvuvsfjK5d1lv+aaUdFno4N3VqIuuVKO6Pjh+UDcGbg89QUlQ6Xlk4qCRiRkzT4NQ==",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@types/rdfjs__namespace/-/rdfjs__namespace-1.1.3.tgz",
"integrity": "sha512-seCWK6z0TqF40ioY7lMhR624I9ovo7xhnhvhAvbWlxfXYtsoQ4QEoddaWFmJDqfat2M1Xv6ThRSN2JGm2OtTIw==",
"requires": {
"@types/rdf-js": "*"
}
......@@ -8785,9 +8785,9 @@
}
},
"clownface": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clownface/-/clownface-1.0.0.tgz",
"integrity": "sha512-8gAAqdD5ueYt62FHE9Zd4OTrcYEfiyQlxV/5NYxzKY5uO/HZbTv1+jhFHgYa3XD7BC+mkg+LFR9Mh168wj9aQQ==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/clownface/-/clownface-1.2.0.tgz",
"integrity": "sha512-01iluBG+GPzFFFvFPhZxuWf8gU8mQVdm6gkvI71xl4fJyJSX7VpX3US4LbMC+0D6+KK75C+Q9AFtYpzv3fKvmg==",
"requires": {
"@rdfjs/data-model": "^1.1.0",
"@rdfjs/namespace": "^1.0.0"
......@@ -15212,11 +15212,6 @@
"integrity": "sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ==",
"dev": true
},
"jquery": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
},
"js-base64": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
......@@ -15425,6 +15420,13 @@
"cross-fetch": "^3.0.6",
"http-link-header": "^1.0.2",
"relative-to-absolute-iri": "^1.0.5"
},
"dependencies": {
"@types/node": {
"version": "13.13.35",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.35.tgz",
"integrity": "sha512-q9aeOGwv+RRou/ca4aJVUM/jD5u7LBexu+rq9PkA/NhHNn8JifcMo94soKm0b6JGSfw/PSNdqtc428OscMvEYA=="
}
}
},
"jsonld-streaming-parser": {
......@@ -17421,9 +17423,9 @@
"dev": true
},
"npm": {
"version": "6.14.10",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.10.tgz",
"integrity": "sha512-FT23Qy/JMA+qxEYReMOr1MY7642fKn8Onn+72LASPi872Owvmw0svm+/DXTHOC3yO9CheEO+EslyXEpdBdRtIA==",
"version": "6.14.11",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.11.tgz",
"integrity": "sha512-1Zh7LjuIoEhIyjkBflSSGzfjuPQwDlghNloppjruOH5bmj9midT9qcNT0tRUZRR04shU9ekrxNy9+UTBrqeBpQ==",
"dev": true,
"requires": {
"JSONStream": "^1.3.5",
......@@ -17463,7 +17465,7 @@
"infer-owner": "^1.0.4",
"inflight": "~1.0.6",
"inherits": "^2.0.4",
"ini": "^1.3.5",
"ini": "^1.3.8",
"init-package-json": "^1.10.3",
"is-cidr": "^3.0.0",
"json-parse-better-errors": "^1.0.2",
......@@ -18830,7 +18832,7 @@
"dev": true
},
"ini": {
"version": "1.3.5",
"version": "1.3.8",
"bundled": true,
"dev": true
},
......@@ -23257,22 +23259,22 @@
}
},
"rdf-validate-shacl": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/rdf-validate-shacl/-/rdf-validate-shacl-0.2.3.tgz",
"integrity": "sha512-CBXwRrkQbKkx7iIsiM8hPLzeWQo5ZrDag9HAMtla46SYt14BS9QnCOD/VcuQYEZVVrxiw80PoOG7qHKxLVOWnw==",
"requires": {
"@rdfjs/dataset": "~1.0.1",
"@rdfjs/namespace": "~1.1.0",
"@rdfjs/term-set": "~1.0.1",
"clownface": "~1.0.0",
"debug": "^4.1.1",
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/rdf-validate-shacl/-/rdf-validate-shacl-0.2.5.tgz",
"integrity": "sha512-Xs9fmGJT7R7YX5NU+7OyW4eVv0zGvPwXfvt8sqS/MaPV07BvKvlKkyzR2OBIYSsiiEvuj5ufcfmKUHvIFSZHCg==",
"requires": {
"@rdfjs/dataset": "^1.0.0",
"@rdfjs/namespace": "^1.0.0",
"@rdfjs/term-set": "^1.0.0",
"clownface": "^1.0.0",
"debug": "^4.0.0",
"rdf-validate-datatype": "^0.1.1"
},
"dependencies": {
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"requires": {
"ms": "2.1.2"
}
......@@ -22,26 +22,27 @@
<script lang="ts">
import Vue from 'vue';
import { validationMixin } from 'vuelidate';
import { required, minLength, maxLength } from 'vuelidate/lib/validators';
import VueI18n from 'vue-i18n';
import BootstrapVue from 'bootstrap-vue';
import Vuelidate from 'vuelidate';
import { validationMixin } from 'vuelidate';
import { required, minLength, maxLength } from 'vuelidate/lib/validators';
import { Quad } from 'rdf-js';
import { LanguageUtil } from '@coscine/app-util';
import FieldReader from './util/FieldReader';
import factory from 'rdf-ext';
import SHACLValidator from 'rdf-validate-shacl';
import rdfParser from "rdf-parse";
import { Readable } from 'stream';
import WrapperInput from './components/WrapperInput.vue';
Vue.use(BootstrapVue);
import LinkedDataHandler from './base/LinkedDataHandler';
Vue.use(VueI18n);
Vue.use(BootstrapVue);
Vue.use(Vuelidate);
const i18n = new VueI18n({
locale: 'en',
......@@ -49,10 +50,7 @@ const i18n = new VueI18n({
silentFallbackWarn: true,
});
import Vuelidate from 'vuelidate';
Vue.use(Vuelidate);
export default Vue.extend({
export default LinkedDataHandler.extend({
i18n,
name: 'FormGenerator',
components: {
......@@ -102,7 +100,8 @@ export default Vue.extend({
},
data() {
return {
quads: [] as any[],
properties: [] as Array<Quad>,
quads: [] as Array<Quad>,
fixedValueIds: [] as any[],
sortedSHACLDefinition: [] as any[],
errorMessages: {} as any,
......@@ -137,9 +136,6 @@ export default Vue.extend({
},
},
methods: {
input() {
this.$emit('input', this.formData);
},
createValidations() {
const me = this;
const validator = {} as any;
......@@ -154,7 +150,7 @@ export default Vue.extend({
return new Promise((resolve, reject) => {
me.timeouts[nodename] = setTimeout(async() => {
// Validate the whole data and check if for the current nodename an error is logged
const report = await me.validatorFunction();
const report = await me.validateMetadata(me.formData, me.quads, me.applicationProfileId);
let resolveValue = true;
if (report.results.some((entry: any) => entry.path.value === nodename)) {
const result = report.results.find((entry: any) => entry.path.value === nodename);
......@@ -171,122 +167,32 @@ export default Vue.extend({
}
this.validations = validator;
},
async validatorFunction() {
const shapes = await this.loadDataset(this.SHACLDefinition, this.mimeType, this.applicationProfileId);
// RDF/JSON => JSON-LD since the loadDataset function doesn't support RDF/JSON
const dataObject = {} as any;
dataObject['@type'] = this.applicationProfileId;
for (const entry of Object.keys(this.formData)) {
for (const metadataEntry of this.formData[entry]) {
if (metadataEntry['value'] !== '') {
if (metadataEntry['type'] === 'uri') {
dataObject[entry] = {
'@id': metadataEntry['value'],
'@type': metadataEntry['datatype']
};
} else {
dataObject[entry] = {
'@value': metadataEntry['value'],
'@type': metadataEntry['datatype']
};
}
}
}
}
const data = await this.loadDataset(JSON.stringify(dataObject), 'application/ld+json', null);
const validator = new SHACLValidator(shapes);
const report = validator.validate(data);
this.$emit('isValid', report.conforms);
return report;
},
async getQuads (data: string, mimeType: string, baseUri: string | null) : Promise<any> {
const input = new Readable({
read: () => {
input.push(data);
input.push(null);
}
});
const quads = [] as any[];
return new Promise((resolve) => {
rdfParser.parse(input, { contentType: mimeType, baseIRI: baseUri })
.on('data', (quad: any) => {
quads.push(quad);
})
.on('end', () => resolve(quads));
});
},
async loadDataset (data: string, mimeType: string, baseUri: string | null) {
const quads = await this.getQuads(data, mimeType, baseUri);
const dataSet = factory.dataset();
for (const quad of quads) {
dataSet.add(quad);
}
return dataSet;
},
checkField(data: any, nodename: string, property: string = 'value') {
return FieldReader.isValueAssignedToKey(data, nodename, property);
},
fieldDefinition(formElement: any) {
if (this.checkField(formElement, 'http://www.w3.org/ns/shacl#order')) {
if (
this.checkField(formElement, 'http://www.w3.org/ns/shacl#datatype')
) {
let datatype = FieldReader.getObject(formElement, 'http://www.w3.org/ns/shacl#datatype');
if (datatype['value'] === 'http://www.w3.org/2001/XMLSchema#string') {
if (
this.checkField(
formElement,
'http://datashapes.org/dash#singleLine'
) &&
FieldReader.getObject(formElement, 'http://datashapes.org/dash#singleLine')[
'value'
] === 'false'
) {
return 'InputTextArea';
} else {
return 'InputTextField';
}
} else if (
datatype['value'] === 'http://www.w3.org/2001/XMLSchema#date'
) {
return 'InputDatePicker';
}
else if (
datatype['value'] === 'http://www.w3.org/2001/XMLSchema#boolean'
) {
return 'InputBooleanCombobox';
}
} else {
if (
this.checkField(formElement, 'http://www.w3.org/ns/shacl#class')
) {
return 'InputCombobox';
}
}
}
return null;
},
async retrieveSHACLDefinition() : Promise<any> {
if (this.SHACLDefinition !== null && this.SHACLDefinition !== '') {
return this.getQuads(this.SHACLDefinition, this.mimeType, this.applicationProfileId);
}
return [];
},
async handleApplicationProfiles() {
this.quads = await this.retrieveSHACLDefinition();
this.quads = await this.retrieveQuads(this.SHACLDefinition, this.mimeType, this.applicationProfileId);
this.properties = this.retrieveProperties(this.SHACLDefinition, this.quads, this.applicationProfileId);
const unmappedSubjects = this.retrievePropertyOrder(this.properties);
this.setPropertySettings(this.properties);
const unmappedSubjects = {} as any;
this.setSHACLDefinition(unmappedSubjects);
this.createValidations();
await this.validateMetadata(this.formData, this.quads, this.applicationProfileId);
},
input() {
this.$emit('input', this.formData);
},
setPropertySettings(properties: Array<Quad>) {
const propertySubjects = this.getPropertySubjects(properties);
this.errorMessages = {} as any;
this.timeouts = {} as any;
for (let i = 0; i < this.quads.length; i++) {
if (this.quads[i].predicate.value === 'http://www.w3.org/ns/shacl#order') {
unmappedSubjects[this.quads[i].object.value] = this.quads.filter((entry) =>
entry.subject.value === this.quads[i].subject.value);
this.errorMessages[this.quads[i].subject.value] = '';
this.timeouts[this.quads[i].subject.value] = null;
}
for (const propertySubject of propertySubjects) {
this.errorMessages[propertySubject] = '';
this.timeouts[propertySubject] = null;
}
},
setSHACLDefinition(unmappedSubjects: any) {
this.fixedValueIds = [];
this.sortedSHACLDefinition = [];
let keys = Object.keys(unmappedSubjects).sort((a, b) => parseInt(a) - parseInt(b));
......@@ -299,10 +205,47 @@ export default Vue.extend({
this.$set(this.formData, nodeName, [{ value: '' }]);
}
}
this.createValidations();
await this.validatorFunction();
},
checkField(data: any, nodename: string, property: string = 'value') {
return FieldReader.isValueAssignedToKey(data, nodename, property);
},
fieldDefinition(formElement: any) {
if (
this.checkField(formElement, 'http://www.w3.org/ns/shacl#datatype')
) {
let datatype = FieldReader.getObject(formElement, 'http://www.w3.org/ns/shacl#datatype');
if (datatype['value'] === 'http://www.w3.org/2001/XMLSchema#string') {
if (
this.checkField(
formElement,
'http://datashapes.org/dash#singleLine'
) &&
FieldReader.getObject(formElement, 'http://datashapes.org/dash#singleLine')[
'value'
] === 'false'
) {
return 'InputTextArea';
} else {
return 'InputTextField';
}
} else if (
datatype['value'] === 'http://www.w3.org/2001/XMLSchema#date'
) {
return 'InputDatePicker';
}
else if (
datatype['value'] === 'http://www.w3.org/2001/XMLSchema#boolean'
) {
return 'InputBooleanCombobox';
}
} else {
if (
this.checkField(formElement, 'http://www.w3.org/ns/shacl#class')
) {
return 'InputCombobox';
}
}
return null;
},
},
});
......
import Vue from 'vue'
import factory from 'rdf-ext';
import SHACLValidator from 'rdf-validate-shacl';
import rdfParser from 'rdf-parse';
import { Readable } from 'stream';
import { Quad } from 'rdf-js';
export default Vue.extend({
data() {
return {
shapes: [] as string[],
};
},
methods: {
async validateMetadata(formData: any, quads: Array<Quad>, applicationProfileId: string) {
// RDF/JSON => JSON-LD since the loadDataset function doesn't support RDF/JSON
const combinedDataObject = [];
const dataObject = {} as any;
dataObject['@type'] = applicationProfileId;
for (const entry of Object.keys(formData)) {
for (const metadataEntry of formData[entry]) {
if (metadataEntry['value'] !== '') {
if (metadataEntry['type'] === 'uri') {
dataObject[entry] = {
'@id': metadataEntry['value'],
'@type': metadataEntry['datatype']
};
} else {
dataObject[entry] = {
'@value': metadataEntry['value'],
'@type': metadataEntry['datatype']
};
}
}
}
}
combinedDataObject.push(dataObject);
// rdfs:subClassOf definitions have to be included for the validation to work with inheritance
const subClasses = quads.filter((quad) => quad.predicate.value === 'http://www.w3.org/2000/01/rdf-schema#subClassOf');
for (const subClassQuad of subClasses) {
combinedDataObject.push({
'@id': subClassQuad.subject.value,
'http://www.w3.org/2000/01/rdf-schema#subClassOf': [
{
'@id': subClassQuad.object.value,
},
],
});
}
// The non-referenced shapes have also to be represented
const nonReferenced = subClasses.filter((quad) =>
!subClasses.some((subQuad) => subQuad.subject.value === quad.object.value));
for (const nonReference of nonReferenced) {
combinedDataObject.push({
'@id': nonReference.object.value,
});
}
const data = await this.loadDataset(JSON.stringify(combinedDataObject), 'application/ld+json', null);
const validator = new SHACLValidator(quads);
const report = validator.validate(data);
this.$emit('isValid', report.conforms);
return report;
},
async getQuads (data: string, mimeType: string, baseUri: string | null) : Promise<Array<Quad>> {
const input = new Readable({
read: () => {
input.push(data);
input.push(null);
}
});
const quads = [] as Array<Quad>;
return new Promise((resolve) => {
rdfParser.parse(input, { contentType: mimeType, baseIRI: baseUri })
.on('data', (quad: Quad) => {
quads.push(quad);
})
.on('end', () => resolve(quads));
});
},
async loadDataset (data: string, mimeType: string, baseUri: string | null) {
const quads = await this.getQuads(data, mimeType, baseUri);
const dataSet = factory.dataset();
for (const quad of quads) {
dataSet.add(quad);
}
return dataSet;
},
fillShapesList(quads: Array<Quad>, currentShape: string) {
this.shapes = [];
this.fillShapesListRecursive(quads, this.shapes, currentShape);
},
fillShapesListRecursive(quads: Array<Quad>, shapes: Array<string>, currentShape: string) {
if (!shapes.includes(currentShape)) {
shapes.push(currentShape);
const subClasses = quads.filter((quad) =>
quad.predicate.value === 'http://www.w3.org/2000/01/rdf-schema#subClassOf'
&& quad.subject.value === currentShape)
.map((quad) => quad.object.value);
for (const subClass of subClasses) {
this.fillShapesListRecursive(quads, shapes, subClass);
}
}
},
async retrieveQuads(SHACLDefinition: string, mimeType: string, applicationProfileId: string) : Promise<Array<Quad>> {
if (SHACLDefinition !== '') {
return this.getQuads(SHACLDefinition, mimeType, applicationProfileId);
}
return [];
},
retrieveProperties(SHACLDefinition: string, quads: Array<Quad>, applicationProfileId: string) : Array<Quad> {
if (SHACLDefinition !== '') {
this.fillShapesList(quads, applicationProfileId);
const propertySets = quads.filter((quad) =>
quad.predicate.value === 'http://www.w3.org/ns/shacl#property'
&& this.shapes.includes(quad.subject.value)
).map((quad) => quad.object.value);
return quads.filter((quad) => propertySets.includes(quad.subject.value));
}
return [];
},
retrievePropertyOrder(properties: Array<Quad>) {
const propertySubjects = this.getPropertySubjects(properties);
let highestOrder = Math.max(...properties.filter((property) =>
property.predicate.value === 'http://www.w3.org/ns/shacl#order'
).map((property) => Number(property.object.value)));
if (highestOrder === -Infinity) {
highestOrder = 0;
}
const unmappedSubjects = {} as any;
let manualOrder = highestOrder + 1;
for (const propertySubject of propertySubjects) {
let currentOrder = 0;
const orderDefinitions = properties.filter((property) =>
property.subject.value === propertySubject
&& property.predicate.value === 'http://www.w3.org/ns/shacl#order');
if (orderDefinitions.length > 0) {
currentOrder = Number(orderDefinitions[0].object.value);
} else {
currentOrder = manualOrder;
manualOrder++;
}
unmappedSubjects[currentOrder] = properties.filter((property) =>
property.subject.value === propertySubject);
}
return unmappedSubjects;
},
getPropertySubjects(properties: Array<Quad>) {
return [...new Set(properties.map((property) => property.subject.value))];
},
}
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment