Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • coscine/frontend/libraries/form-generator
1 result
Select Git revision
Show changes
Commits on Source (5)
{
"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"
}
{
"name": "@coscine/form-generator",
"version": "1.10.2",
"version": "1.11.0",
"main": "./dist/index.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
......@@ -35,12 +35,13 @@
"@coscine/api-connection": "^1.23.1",
"@coscine/app-util": "^1.7.1",
"@types/jquery": "^3.5.2",
"@types/node": "^14.14.12",
"@types/rdf-js": "^4.0.0",
"@types/vuelidate": "^0.7.13",
"bootstrap-vue": "^2.20.1",
"jquery": "^3.5.1",
"rdf-ext": "^1.3.0",
"rdf-parse": "^1.5.0",
"rdf-validate-shacl": "^0.2.3",
"rdf-validate-shacl": "^0.2.5",
"vue": "^2.6.12",
"vue-i18n": "^8.22.0",
"vue-material-design-icons": "^4.9.0",
......
......@@ -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))];
},
}
});