From d0a260dabd086996b0fd24f9ed7f20be88224ad1 Mon Sep 17 00:00:00 2001
From: mdelez <60604010+mdelez@users.noreply.github.com>
Date: Tue, 22 Mar 2022 16:11:08 +0100
Subject: [PATCH] feat(resource-instance): add subclass creation support
(DEV-553) (#686)
* feat(new-resource-instance-form): add support for creating subclasses during the creation of a resource
* refactor: rename ontoIri to currentOntoIri to fix tests
* test(link-value): fix link value tests
* chore(config): add ls-test-server config
* chore(cleanup): remove console logs
* chore(link-value): add colon to clarify label
---
angular.json | 22 +++++++
package.json | 1 +
src/app/main/dialog/dialog.component.html | 2 +-
src/app/main/dialog/dialog.component.ts | 1 +
.../property-form/property-form.component.ts | 1 -
.../create-link-resource.component.html | 3 +-
.../create-link-resource.component.ts | 2 +-
.../display-edit/display-edit.component.html | 2 +-
.../display-edit/display-edit.component.ts | 4 +-
.../resource-instance-form.component.html | 3 +-
.../select-properties.component.html | 3 +-
.../select-properties.component.spec.ts | 9 ++-
.../select-properties.component.ts | 10 ++--
.../switch-properties.component.html | 2 +-
.../switch-properties.component.ts | 5 +-
.../link-value/link-value.component.html | 4 +-
.../link-value/link-value.component.spec.ts | 58 ++++++++++++++++++-
.../values/link-value/link-value.component.ts | 35 ++++++++++-
src/config/config.ls-test-server.json | 29 ++++++++++
.../environment.ls-test-server.ts | 19 ++++++
20 files changed, 190 insertions(+), 25 deletions(-)
create mode 100644 src/config/config.ls-test-server.json
create mode 100644 src/environments/environment.ls-test-server.ts
diff --git a/angular.json b/angular.json
index 6976208117..3dac7cb86c 100644
--- a/angular.json
+++ b/angular.json
@@ -106,6 +106,22 @@
"vendorChunk": false,
"buildOptimizer": false,
"budgets": []
+ },
+ "ls-test-server": {
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.ls-test-server.ts"
+ }
+ ],
+ "optimization": false,
+ "sourceMap": true,
+ "namedChunks": true,
+ "aot": true,
+ "extractLicenses": false,
+ "vendorChunk": false,
+ "buildOptimizer": false,
+ "budgets": []
}
}
},
@@ -123,6 +139,9 @@
},
"test-server": {
"browserTarget": "dsp-app:build:test-server"
+ },
+ "ls-test-server": {
+ "browserTarget": "dsp-app:build:ls-test-server"
}
}
},
@@ -176,6 +195,9 @@
},
"test-server": {
"devServerTarget": "dsp-app:serve:test-server"
+ },
+ "ls-test-server": {
+ "devServerTarget": "dsp-app:serve:ls-test-server"
}
}
}
diff --git a/package.json b/package.json
index 2b5bdc5d88..e1f2176bab 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"ng": "ng",
"start": "ng serve",
"start-with-test-server": "ng serve --configuration test-server",
+ "start-with-ls-test-server": "ng serve --configuration ls-test-server",
"lint-ci": "eslint --color -c .eslintrc.js --ext .ts ./src/app",
"lint-local": "eslint --color --fix -c .eslintrc.js --ext .ts ./src/app",
"e2e": "ng e2e",
diff --git a/src/app/main/dialog/dialog.component.html b/src/app/main/dialog/dialog.component.html
index 3c7207ab8b..ec28359b5f 100644
--- a/src/app/main/dialog/dialog.component.html
+++ b/src/app/main/dialog/dialog.component.html
@@ -402,7 +402,7 @@
diff --git a/src/app/main/dialog/dialog.component.ts b/src/app/main/dialog/dialog.component.ts
index 3e2bbac7e9..8037e4abef 100644
--- a/src/app/main/dialog/dialog.component.ts
+++ b/src/app/main/dialog/dialog.component.ts
@@ -22,6 +22,7 @@ export interface DialogData {
selectedResources?: FilteredResources;
resourceClassDefinition?: string;
fullSize?: boolean;
+ ontoIri?: string;
}
export interface ConfirmationWithComment {
diff --git a/src/app/project/ontology/property-form/property-form.component.ts b/src/app/project/ontology/property-form/property-form.component.ts
index 36a58646c7..f77ce01b66 100644
--- a/src/app/project/ontology/property-form/property-form.component.ts
+++ b/src/app/project/ontology/property-form/property-form.component.ts
@@ -424,7 +424,6 @@ export class PropertyFormComponent implements OnInit {
this._dspApiConnection.v2.onto.updateResourceProperty(onto4Comment).subscribe(
(classCommentResponse: ResourcePropertyDefinitionWithAllLanguages) => {
- console.log(classCommentResponse);
this.lastModificationDate = classCommentResponse.lastModificationDate;
if (!this.unsupportedPropertyType) {
diff --git a/src/app/workspace/resource/operations/create-link-resource/create-link-resource.component.html b/src/app/workspace/resource/operations/create-link-resource/create-link-resource.component.html
index b2265cb915..b583438a0e 100644
--- a/src/app/workspace/resource/operations/create-link-resource/create-link-resource.component.html
+++ b/src/app/workspace/resource/operations/create-link-resource/create-link-resource.component.html
@@ -8,9 +8,10 @@
diff --git a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.spec.ts b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.spec.ts
index 73c3886622..14b6b944db 100644
--- a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.spec.ts
+++ b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.spec.ts
@@ -21,9 +21,10 @@ import { SwitchPropertiesComponent } from './switch-properties/switch-properties
+ [parentForm]="propertiesParentForm"
+ [currentOntoIri]="currentOntoIri">
`
})
class TestSelectPropertiesParentComponent implements OnInit {
@@ -38,6 +39,8 @@ class TestSelectPropertiesParentComponent implements OnInit {
propertiesParentForm: FormGroup;
+ currentOntoIri: string;
+
constructor(private _fb: FormBuilder) { }
ngOnInit() {
@@ -48,6 +51,8 @@ class TestSelectPropertiesParentComponent implements OnInit {
this.selectedResourceClass = this.ontoInfo.classes['http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'];
this.properties = this.ontoInfo.getPropertyDefinitionsByType(ResourcePropertyDefinition).filter(prop => prop.isEditable && !prop.isLinkProperty);
+
+ this.currentOntoIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2';
}
}
diff --git a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts
index 70313b0b1d..0afdfa3691 100644
--- a/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts
+++ b/src/app/workspace/resource/resource-instance-form/select-properties/select-properties.component.ts
@@ -28,10 +28,12 @@ export class SelectPropertiesComponent implements OnInit {
@Input() ontologyInfo: ResourceClassAndPropertyDefinitions;
- @Input() resourceClass: ResourceClassDefinition;
+ @Input() selectedResourceClass: ResourceClassDefinition;
@Input() parentForm: FormGroup;
+ @Input() currentOntoIri: string;
+
parentResource = new ReadResource();
index = 0;
@@ -79,7 +81,7 @@ export class SelectPropertiesComponent implements OnInit {
return CardinalityUtil.createValueForPropertyAllowed(
prop.id,
this.propertyValuesKeyValuePair[prop.id].length,
- this.ontologyInfo.classes[this.resourceClass.id]
+ this.ontologyInfo.classes[this.selectedResourceClass.id]
);
}
@@ -91,8 +93,8 @@ export class SelectPropertiesComponent implements OnInit {
* @param propId property id
*/
isPropRequired(propId: string): boolean {
- if (this.resourceClass !== undefined && propId) {
- this.resourceClass.propertiesList.filter(
+ if (this.selectedResourceClass !== undefined && propId) {
+ this.selectedResourceClass.propertiesList.filter(
(card: IHasProperty) => {
if (card.propertyIndex === propId) {
// cardinality 1 or 1-N
diff --git a/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.html b/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.html
index c059fff86f..c2e41496ab 100644
--- a/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.html
+++ b/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.html
@@ -10,7 +10,7 @@
+ [parentResource]="parentResource" [propIri]="property.id" [currentOntoIri]="currentOntoIri">
diff --git a/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.ts b/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.ts
index a7ef3589cd..7cee92d9fd 100644
--- a/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.ts
+++ b/src/app/workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component.ts
@@ -1,6 +1,6 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { FormGroup } from '@angular/forms';
-import { Constants, ReadResource, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js';
+import { Constants, ReadResource, ResourceClassDefinition, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js';
import { BaseValueDirective } from 'src/app/main/directive/base-value.directive';
import { ValueService } from '../../../services/value.service';
@@ -23,6 +23,8 @@ export class SwitchPropertiesComponent implements OnInit {
@Input() isRequiredProp: boolean;
+ @Input() currentOntoIri?: string;
+
mode = 'create';
// gui element in case of textValue
@@ -35,7 +37,6 @@ export class SwitchPropertiesComponent implements OnInit {
) { }
ngOnInit(): void {
-
// the input isRequiredProp provided by KeyValuePair is stored as a number
// a conversion from a number to a boolean is required by the input valueRequiredValidator
this.isRequiredProp = !!+this.isRequiredProp;
diff --git a/src/app/workspace/resource/values/link-value/link-value.component.html b/src/app/workspace/resource/values/link-value/link-value.component.html
index 04d2e1f9e0..fe51ba3dd6 100644
--- a/src/app/workspace/resource/values/link-value/link-value.component.html
+++ b/src/app/workspace/resource/values/link-value/link-value.component.html
@@ -9,8 +9,8 @@
-
- Create New {{resourceClassLabel}}
+
+ Create New: {{rc?.label}}
{{res?.label}}
diff --git a/src/app/workspace/resource/values/link-value/link-value.component.spec.ts b/src/app/workspace/resource/values/link-value/link-value.component.spec.ts
index 3e727b13be..821dfba40b 100644
--- a/src/app/workspace/resource/values/link-value/link-value.component.spec.ts
+++ b/src/app/workspace/resource/values/link-value/link-value.component.spec.ts
@@ -11,6 +11,7 @@ import {
MockOntology,
MockResource,
ReadLinkValue,
+ ReadOntology,
ReadResource,
ReadResourceSequence,
SearchEndpointV2,
@@ -27,7 +28,7 @@ import { LinkValueComponent } from './link-value.component';
@Component({
template: `
`
+ [propIri]="propIri" [currentOntoIri]="currentOntoIri" (referredResourceClicked)="refResClicked($event)" (referredResourceHovered)="refResHovered($event)">`
})
class TestHostDisplayValueComponent implements OnInit {
@@ -36,6 +37,7 @@ class TestHostDisplayValueComponent implements OnInit {
displayInputVal: ReadLinkValue;
parentResource: ReadResource;
propIri: string;
+ currentOntoIri: string;
mode: 'read' | 'update' | 'create' | 'search';
linkValueClicked: ReadLinkValue;
linkValueHovered: ReadLinkValue;
@@ -50,6 +52,7 @@ class TestHostDisplayValueComponent implements OnInit {
this.propIri = this.displayInputVal.property;
this.parentResource = res;
this.mode = 'read';
+ this.currentOntoIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2';
});
}
@@ -68,19 +71,21 @@ class TestHostDisplayValueComponent implements OnInit {
*/
@Component({
template: `
- `
+ `
})
class TestHostCreateValueComponent implements OnInit {
@ViewChild('inputVal') inputValueComponent: LinkValueComponent;
parentResource: ReadResource;
propIri: string;
+ currentOntoIri: string;
mode: 'read' | 'update' | 'create' | 'search';
ngOnInit() {
MockResource.getTestThing().subscribe(res => {
this.propIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThingValue';
+ this.currentOntoIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2';
this.parentResource = res;
this.mode = 'create';
});
@@ -92,18 +97,20 @@ class TestHostCreateValueComponent implements OnInit {
*/
@Component({
template: `
- `
+ `
})
class TestHostCreateValueNoValueRequiredComponent implements OnInit {
@ViewChild('inputVal') inputValueComponent: LinkValueComponent;
parentResource: ReadResource;
propIri: string;
+ currentOntoIri: string;
mode: 'read' | 'update' | 'create' | 'search';
ngOnInit() {
MockResource.getTestThing().subscribe(res => {
this.propIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThingValue';
+ this.currentOntoIri = 'http://0.0.0.0:3333/ontology/0001/anything/v2';
this.parentResource = res;
this.mode = 'create';
});
@@ -158,6 +165,21 @@ describe('LinkValueComponent', () => {
const dspConnSpy = TestBed.inject(DspApiConnectionToken);
+ (dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getOntology.and.callFake(
+ (ontoIri: string) => {
+
+ const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2');
+ const knoraApiOnto = MockOntology.mockReadOntology('http://api.knora.org/ontology/knora-api/v2');
+
+ const ontoMap: Map = new Map();
+
+ ontoMap.set('http://api.knora.org/ontology/knora-api/v2', knoraApiOnto);
+ ontoMap.set('http://0.0.0.0:3333/ontology/0001/anything/v2', anythingOnto);
+
+ return of(ontoMap);
+ }
+ );
+
(dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getResourceClassDefinition.and.callFake(
(resClassIri: string) => of(MockOntology.mockIResourceClassAndPropertyDefinitions('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'))
);
@@ -497,6 +519,21 @@ describe('LinkValueComponent', () => {
const dspConnSpy = TestBed.inject(DspApiConnectionToken);
+ (dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getOntology.and.callFake(
+ (ontoIri: string) => {
+
+ const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2');
+ const knoraApiOnto = MockOntology.mockReadOntology('http://api.knora.org/ontology/knora-api/v2');
+
+ const ontoMap: Map = new Map();
+
+ ontoMap.set('http://api.knora.org/ontology/knora-api/v2', knoraApiOnto);
+ ontoMap.set('http://0.0.0.0:3333/ontology/0001/anything/v2', anythingOnto);
+
+ return of(ontoMap);
+ }
+ );
+
(dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getResourceClassDefinition.and.callFake(
(resClassIri: string) => of(MockOntology.mockIResourceClassAndPropertyDefinitions('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'))
);
@@ -633,6 +670,21 @@ describe('LinkValueComponent', () => {
const dspConnSpy = TestBed.inject(DspApiConnectionToken);
+ (dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getOntology.and.callFake(
+ (ontoIri: string) => {
+
+ const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2');
+ const knoraApiOnto = MockOntology.mockReadOntology('http://api.knora.org/ontology/knora-api/v2');
+
+ const ontoMap: Map = new Map();
+
+ ontoMap.set('http://api.knora.org/ontology/knora-api/v2', knoraApiOnto);
+ ontoMap.set('http://0.0.0.0:3333/ontology/0001/anything/v2', anythingOnto);
+
+ return of(ontoMap);
+ }
+ );
+
(dspConnSpy.v2.ontologyCache as jasmine.SpyObj).getResourceClassDefinition.and.callFake(
(resClassIri: string) => of(MockOntology.mockIResourceClassAndPropertyDefinitions('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'))
);
diff --git a/src/app/workspace/resource/values/link-value/link-value.component.ts b/src/app/workspace/resource/values/link-value/link-value.component.ts
index 92a00cf497..5a4652563b 100644
--- a/src/app/workspace/resource/values/link-value/link-value.component.ts
+++ b/src/app/workspace/resource/values/link-value/link-value.component.ts
@@ -15,9 +15,12 @@ import {
CreateLinkValue,
KnoraApiConnection,
ReadLinkValue,
+ ReadOntology,
ReadResource,
ReadResourceSequence,
ResourceClassAndPropertyDefinitions,
+ ResourceClassDefinition,
+ ResourcePropertyDefinition,
UpdateLinkValue
} from '@dasch-swiss/dsp-js';
import { Subscription } from 'rxjs';
@@ -43,6 +46,7 @@ export class LinkValueComponent extends BaseValueDirective implements OnInit, On
@Input() displayValue?: ReadLinkValue;
@Input() parentResource: ReadResource;
@Input() propIri: string;
+ @Input() currentOntoIri: string;
@Output() referredResourceClicked: EventEmitter = new EventEmitter();
@@ -60,6 +64,9 @@ export class LinkValueComponent extends BaseValueDirective implements OnInit, On
// label cannot contain logical operations of lucene index
customValidators = [resourceValidator];
+ resourceClasses: ResourceClassDefinition[];
+ properties: ResourcePropertyDefinition[];
+
constructor(
private _dialog: MatDialog,
@Inject(FormBuilder) private _fb: FormBuilder,
@@ -117,7 +124,29 @@ export class LinkValueComponent extends BaseValueDirective implements OnInit, On
// get label of resource class
this._dspApiConnection.v2.ontologyCache.getResourceClassDefinition(this.restrictToResourceClass).subscribe(
- (onto: ResourceClassAndPropertyDefinitions) => this.resourceClassLabel = onto.classes[this.restrictToResourceClass].label
+ (onto: ResourceClassAndPropertyDefinitions) => {
+ this.resourceClassLabel = onto.classes[this.restrictToResourceClass].label;
+ }
+ );
+
+ this._dspApiConnection.v2.ontologyCache.getOntology(this.currentOntoIri).subscribe(
+ (onto: Map) => {
+ const resClasses = onto.get(this.currentOntoIri).getClassDefinitionsByType(ResourceClassDefinition);
+ this.resourceClasses = resClasses.filter(
+ (resClassDef: ResourceClassDefinition) => resClassDef.id === this.restrictToResourceClass
+ );
+ const subclasses = resClasses.filter(
+ (resClassDef: ResourceClassDefinition) =>
+ resClassDef.subClassOf.indexOf(this.restrictToResourceClass) > -1
+ );
+
+ this.resourceClasses = this.resourceClasses.concat(subclasses);
+
+ this.properties = onto.get(this.currentOntoIri).getPropertyDefinitionsByType(ResourcePropertyDefinition);
+ },
+ error => {
+ console.error(error);
+ }
);
// initialize form control elements
@@ -214,7 +243,7 @@ export class LinkValueComponent extends BaseValueDirective implements OnInit, On
this.referredResourceHovered.emit(this.displayValue);
}
- openDialog(mode: string, ev: Event, iri?: string): void {
+ openDialog(mode: string, ev: Event, iri?: string, resClass?: ResourceClassDefinition): void {
ev.preventDefault();
const dialogConfig: MatDialogConfig = {
width: '840px',
@@ -222,7 +251,7 @@ export class LinkValueComponent extends BaseValueDirective implements OnInit, On
position: {
top: '112px'
},
- data: { mode: mode, title: this.resourceClassLabel, id: iri, parentResource: this.parentResource, resourceClassDefinition: this.restrictToResourceClass },
+ data: { mode: mode, title: resClass.label, id: iri, parentResource: this.parentResource, resourceClassDefinition: resClass.id, ontoIri: this.currentOntoIri },
disableClose: true
};
diff --git a/src/config/config.ls-test-server.json b/src/config/config.ls-test-server.json
new file mode 100644
index 0000000000..7dedba0692
--- /dev/null
+++ b/src/config/config.ls-test-server.json
@@ -0,0 +1,29 @@
+{
+ "dspRelease": "2022.01.01",
+ "apiProtocol": "https",
+ "apiHost": "api.ls-test-server.dasch.swiss",
+ "apiPort": 443,
+ "apiPath": "",
+ "iiifProtocol": "https",
+ "iiifHost": "iiif.ls-test-server.dasch.swiss",
+ "iiifPort": 443,
+ "iiifPath": "",
+ "geonameToken": "knora",
+ "jsonWebToken": "",
+ "logErrors": true,
+ "iriBase": "http://rdfh.ch",
+ "instrumentation": {
+ "environment": "ls-test-server",
+ "dataDog": {
+ "enabled": false,
+ "applicationId": "",
+ "clientToken": "",
+ "site": "",
+ "service": ""
+ },
+ "rollbar": {
+ "enabled": false,
+ "accessToken": ""
+ }
+ }
+}
diff --git a/src/environments/environment.ls-test-server.ts b/src/environments/environment.ls-test-server.ts
new file mode 100644
index 0000000000..510d5735c8
--- /dev/null
+++ b/src/environments/environment.ls-test-server.ts
@@ -0,0 +1,19 @@
+/*
+ * This file can be replaced during build by using the `fileReplacements` array.
+ * `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+ * The list of file replacements can be found in `angular.json`.
+ */
+
+export const environment = {
+ name: 'ls-test-server',
+ production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.