Skip to content

Commit

Permalink
feat(ontology): check if an ontology, a class or a property can be de…
Browse files Browse the repository at this point in the history
…leted (DSP-1750) (#457)

* docs(ontology): fix 404 error on image

* chore(deps): bump dsp-js version to 2.4.0

* feat(ontology): disable delete class button

* refactor(ontology): clean up code

* refactor(ontology): consistent submit button

* feat(ontology): disable delete ontology button

* fix(ontology): use the right method

* feat(ontology): ask if res class can be deleted

* test(ontology): fix tests

* test(ontology): bug fix in test

* test(ontology): bug fix in test

* chore(ontology): undo previous changes

* test(ontology): fix the test

* test(ontology): run all the tests

* test(ontology): add test to new method

* test(ontology): enable all tests

* test(ontology): set correct test environment

test(ontology): enable all tests

* feat(ontology): check if prop can be deleted

* test(ontology): enable all tests

* fix(ontology): propCanBeDeleted shouldn't be in the cardinality setup
  • Loading branch information
kilchenmann committed Jun 22, 2021
1 parent 6415152 commit fb0c275
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 53 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -33,7 +33,7 @@
"@angular/platform-browser-dynamic": "^11.2.9",
"@angular/router": "^11.2.9",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^2.3.0",
"@dasch-swiss/dsp-js": "^2.4.0",
"@dasch-swiss/dsp-ui": "^1.4.0",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "5.0.0",
Expand Down
@@ -1,6 +1,4 @@
<dsp-progress-indicator *ngIf="loading"></dsp-progress-indicator>

<form [formGroup]="ontologyForm" (ngSubmit)="submitData()" class="form" *ngIf="!loading">
<form [formGroup]="ontologyForm" (ngSubmit)="submitData()" class="form">
<!-- auto complete list to select resource classes -->
<div class="form-content">
<mat-form-field class="large-field ontology-name">
Expand Down
Expand Up @@ -223,6 +223,7 @@ export class OntologyFormComponent implements OnInit {
this._dspApiConnection.v2.onto.updateOntology(ontologyData).subscribe(
(response: OntologyMetadata) => {
this.updateParent.emit(response.id);
this.loading = false;
this.closeDialog.emit(response.id);
},
(error: ApiResponseError) => {
Expand All @@ -246,6 +247,7 @@ export class OntologyFormComponent implements OnInit {
this._dspApiConnection.v2.onto.createOntology(ontologyData).subscribe(
(response: OntologyMetadata) => {
this.updateParent.emit(response.id);
this.loading = false;
this.closeDialog.emit(response.id);
},
(error: ApiResponseError) => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/project/ontology/ontology.component.html
Expand Up @@ -109,8 +109,8 @@ <h2 class="mat-title">
</button>
</span>
<span
[matTooltip]="(ontology.lastModificationDate ? 'Delete data model' : 'This data model can\'t be edited because of missing lastModificationDate!')">
<button mat-button [disabled]="!ontology.lastModificationDate"
[matTooltip]="(ontology.lastModificationDate ? (ontologyCanBeDeleted ? 'Delete data model' : 'This data model can\'t be deleted because it is in use!') : 'This data model can\'t be deleted because of missing lastModificationDate!')">
<button mat-button [disabled]="!ontology.lastModificationDate || !ontologyCanBeDeleted"
(click)="$event.stopPropagation(); delete('Ontology', {iri: ontologyIri, label: ontology.label})">
<mat-icon>delete</mat-icon>
Delete
Expand Down
106 changes: 94 additions & 12 deletions src/app/project/ontology/ontology.component.spec.ts
@@ -1,5 +1,5 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { MatOptionModule } from '@angular/material/core';
Expand All @@ -14,34 +14,53 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { KnoraApiConnection } from '@dasch-swiss/dsp-js';
import { ApiResponseData, CanDoResponse, ListNodeInfo, ListsEndpointAdmin, ListsResponse, MockList, MockOntology, OntologiesEndpointV2, OntologiesMetadata, ReadOntology } from '@dasch-swiss/dsp-js';
import {
AppInitService,
DspActionModule,
DspApiConfigToken,
DspApiConnectionToken,
DspActionModule, DspApiConnectionToken,
DspCoreModule
} from '@dasch-swiss/dsp-ui';
import { of } from 'rxjs';
import { AjaxResponse } from 'rxjs/ajax';
import { CacheService } from 'src/app/main/cache/cache.service';
import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorComponent } from 'src/app/main/error/error.component';
import { TestConfig } from 'test.config';
import { OntologyVisualizerComponent } from './ontology-visualizer/ontology-visualizer.component';
import { OntologyComponent } from './ontology.component';
import { PropertyInfoComponent } from './property-info/property-info.component';
import { ResourceClassInfoComponent } from './resource-class-info/resource-class-info.component';

describe('OntologyComponent', () => {
let component: OntologyComponent;
let fixture: ComponentFixture<OntologyComponent>;

beforeEach(waitForAsync(() => {
const ontologyEndpointSpyObj = {
admin: {
listsEndpoint: jasmine.createSpyObj('listsEndpoint', ['getListsInProject'])
},
v2: {
onto: jasmine.createSpyObj('onto', [
'getOntologiesByProjectIri',
'getOntology',
'canDeleteOntology',
'deleteOntology',
'deleteResourceClass',
'deleteResourceProperty'
])
}
};

const cacheServiceSpy = jasmine.createSpyObj('CacheService', ['get', 'set']);

TestBed.configureTestingModule({
declarations: [
OntologyComponent,
OntologyVisualizerComponent,
DialogComponent,
ErrorComponent,
PropertyInfoComponent
PropertyInfoComponent,
ResourceClassInfoComponent
],
imports: [
BrowserAnimationsModule,
Expand Down Expand Up @@ -76,14 +95,13 @@ describe('OntologyComponent', () => {
}
}
},
AppInitService,
{
provide: DspApiConfigToken,
useValue: TestConfig.ApiConfig
provide: DspApiConnectionToken,
useValue: ontologyEndpointSpyObj
},
{
provide: DspApiConnectionToken,
useValue: new KnoraApiConnection(TestConfig.ApiConfig)
provide: CacheService,
useValue: cacheServiceSpy
}
]
})
Expand Down Expand Up @@ -111,8 +129,72 @@ describe('OntologyComponent', () => {
});

beforeEach(() => {
// set local storage session data
localStorage.setItem('session', JSON.stringify(TestConfig.CurrentSession));

// set cache with current ontology
const cacheSpy = TestBed.inject(CacheService);

(cacheSpy as jasmine.SpyObj<CacheService>).get.and.callFake(
() => {
const response: ReadOntology = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2');
return of(response);
}
);

// can delete ontology request
const dspConnSpy = TestBed.inject(DspApiConnectionToken);
(dspConnSpy.v2.onto as jasmine.SpyObj<OntologiesEndpointV2>).canDeleteOntology.and.callFake(
() => {
const deleteResClass: CanDoResponse = {
'canDo': false
};

return of(deleteResClass);
}
);

(dspConnSpy.v2.onto as jasmine.SpyObj<OntologiesEndpointV2>).getOntologiesByProjectIri.and.callFake(
() => {
const response: OntologiesMetadata = MockOntology.mockOntologiesMetadata();
return of(response);
}
);

(dspConnSpy.v2.onto as jasmine.SpyObj<OntologiesEndpointV2>).getOntology.and.callFake(
() => {
const response: ReadOntology = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2');
return of(response);
}
);

(dspConnSpy.admin.listsEndpoint as jasmine.SpyObj<ListsEndpointAdmin>).getListsInProject.and.callFake(
() => {
const response = new ListsResponse();

response.lists = new Array<ListNodeInfo>();

const mockList1 = new ListNodeInfo();
mockList1.comments = [];
mockList1.id = 'http://rdfh.ch/lists/0001/mockList01';
mockList1.isRootNode = true;
mockList1.labels = [{ language: 'en', value: 'Mock List 01' }];
mockList1.projectIri = 'http://rdfh.ch/projects/myProjectIri';

const mockList2 = new ListNodeInfo();
mockList2.comments = [];
mockList2.id = 'http://rdfh.ch/lists/0001/mockList02';
mockList2.isRootNode = true;
mockList2.labels = [{ language: 'en', value: 'Mock List 02' }];
mockList2.projectIri = 'http://rdfh.ch/projects/myProjectIri';

response.lists.push(mockList1, mockList2);

return of(ApiResponseData.fromAjaxResponse({ response } as AjaxResponse));
}
);


fixture = TestBed.createComponent(OntologyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
Expand Down
13 changes: 13 additions & 0 deletions src/app/project/ontology/ontology.component.ts
Expand Up @@ -6,6 +6,7 @@ import { ActivatedRoute, Params, Router } from '@angular/router';
import {
ApiResponseData,
ApiResponseError,
CanDoResponse,
ClassDefinition,
Constants,
DeleteOntologyResponse,
Expand Down Expand Up @@ -81,6 +82,8 @@ export class OntologyComponent implements OnInit {
// when updating something inside the ontology
lastModificationDate: string;

ontologyCanBeDeleted: boolean;

// all resource classes in the current ontology
ontoClasses: ClassDefinition[];
// expand the resource class cards
Expand Down Expand Up @@ -344,6 +347,16 @@ export class OntologyComponent implements OnInit {
// grab the onto properties information to display
this.initOntoProperties(ontology.getAllPropertyDefinitions());

// check if the ontology can be deleted
this._dspApiConnection.v2.onto.canDeleteOntology(this.ontology.id).subscribe(
(response: CanDoResponse) => {
this.ontologyCanBeDeleted = response.canDo;
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);

this.loadOntology = false;
}

Expand Down
Expand Up @@ -52,7 +52,7 @@
<mat-icon>tune</mat-icon>
</button>
<span
[matTooltip]="(resClasses.length > 0 ? 'The property can\'t be removed because it\'s in use' : 'Remove property from resource class')">
[matTooltip]="((resClasses.length > 0) ? 'The property can\'t be removed because it\'s in use' : 'Remove property from resource class')">
<!-- TODO: add "disabled" param as soon as we use DSP-API 13.9.1 and DSP-JS 2.4.0; use same value in matTooltip above -->
<button mat-button class="delete"
(click)="removePropertyFromClass.emit({iri: propDef.id, label: propDef.label})">
Expand All @@ -67,7 +67,7 @@
<mat-icon>edit</mat-icon>
</button>
<span
[matTooltip]="(resClasses.length > 0 ? 'The property can\'t be deleted because it\'s used in a class' : 'Delete property')">
[matTooltip]="((resClasses.length > 0 || !propCanBeDeleted)? 'The property can\'t be deleted because it\'s used in a class' : 'Delete property')">
<button mat-button [disabled]="resClasses.length > 0" class="delete"
(click)="deleteResourceProperty.emit({iri: propDef.id, label: propDef.label})">
<mat-icon>delete</mat-icon>
Expand Down
Expand Up @@ -2,7 +2,7 @@ import { OverlayContainer } from '@angular/cdk/overlay';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { Component, DebugElement, ViewChild } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogHarness } from '@angular/material/dialog/testing';
Expand All @@ -12,8 +12,17 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Constants, IHasProperty, ListNodeInfo, MockOntology, ReadOntology, ResourcePropertyDefinitionWithAllLanguages } from '@dasch-swiss/dsp-js';
import { DspActionModule } from '@dasch-swiss/dsp-ui';
import {
CanDoResponse,
Constants,
IHasProperty,
ListNodeInfo,
MockOntology,
OntologiesEndpointV2,
ReadOntology,
ResourcePropertyDefinitionWithAllLanguages
} from '@dasch-swiss/dsp-js';
import { DspActionModule, DspApiConnectionToken } from '@dasch-swiss/dsp-ui';
import { of } from 'rxjs';
import { CacheService } from 'src/app/main/cache/cache.service';
import { DialogHeaderComponent } from 'src/app/main/dialog/dialog-header/dialog-header.component';
Expand Down Expand Up @@ -170,6 +179,12 @@ describe('PropertyInfoComponent', () => {

const cacheServiceSpy = jasmine.createSpyObj('CacheService', ['get']);

const ontologyEndpointSpyObj = {
v2: {
onto: jasmine.createSpyObj('onto', ['canDeleteResourceProperty'])
}
};

TestBed.configureTestingModule({
declarations: [
DialogComponent,
Expand All @@ -191,6 +206,10 @@ describe('PropertyInfoComponent', () => {
MatTooltipModule
],
providers: [
{
provide: DspApiConnectionToken,
useValue: ontologyEndpointSpyObj
},
{
provide: CacheService,
useValue: cacheServiceSpy
Expand All @@ -208,6 +227,19 @@ describe('PropertyInfoComponent', () => {
.compileComponents();
}));

beforeEach(() => {
const dspConnSpy = TestBed.inject(DspApiConnectionToken);
(dspConnSpy.v2.onto as jasmine.SpyObj<OntologiesEndpointV2>).canDeleteResourceProperty.and.callFake(
() => {
const deleteResProp: CanDoResponse = {
'canDo': false
};

return of(deleteResProp);
}
);
});

beforeEach(() => {
simpleTextHostFixture = TestBed.createComponent(SimpleTextHostComponent);
simpleTextHostComponent = simpleTextHostFixture.componentInstance;
Expand Down

0 comments on commit fb0c275

Please sign in to comment.