Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ontology Cache: Ignore third-party properties consistently #183

Merged
merged 12 commits into from
May 14, 2020
Merged
19 changes: 18 additions & 1 deletion design-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,21 @@ Caching is necessary to avoid making redundant calls to the Knora API and proces

`ListNodeV2Cache` caches v2 list nodes. As an optimization, the entire list is regarded as a dependency of any given list node and requested. Like this, all list nodes can be fetched with one request and written to the cache.

````
### Tests

Component have their own spec files defining unit tests.
Since this library relies on Knora API,
static test data is generated from Knora API and integrated automatically.

Actual calls to the Knora API in production are mocked with `jasmine.Ajax`.
The data is read from the static test data files and passed to `jasmine.Ajax`,
so the component under test can react to it.

If a component A depends on a component B, then B has to be mocked during the test using a `jasmine.Spy`.
The behavior of component B is simulated and a fake response is returned.
Mocking components turned out to be quite complex for the `OntologyCache`.
Therefore, the mock has been made a stand-alone testing component called `MockOntology` that can be reused.
It can also be used to generate static test data in software projects using this library.

Since `MockOntology` is complex, some global assertions are made to guarantee that the mock behaves as the actual component.
These assertions are defined in `MockOntologyAssertions`.
8 changes: 8 additions & 0 deletions src/cache/OntologyCache.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { of } from "rxjs";
import { MockOntologyAssertions } from "../../test/data/api/v2/mock-ontology-assertions";
import { MockOntology } from "../../test/data/api/v2/mockOntology";
import { KnoraApiConfig } from "../knora-api-config";
import { KnoraApiConnection } from "../knora-api-connection";
Expand Down Expand Up @@ -165,7 +166,14 @@ describe("OntologyCache", () => {
resClassDef => {

expect(resClassDef.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"] instanceof ResourceClassDefinition).toBeTruthy();

expect(resClassDef.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"].propertiesList.length).toEqual(38);
expect(resClassDef.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"].propertiesList.map(prop => prop.propertyIndex).sort()).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.sort());
expect(resClassDef.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"].propertiesList.length).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.length);

expect(Object.keys(resClassDef.properties).length).toEqual(38);
expect(Object.keys(resClassDef.properties).sort()).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.sort());
expect(Object.keys(resClassDef.properties).length).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.length);

done();

Expand Down
13 changes: 10 additions & 3 deletions src/cache/OntologyCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,25 @@ export class OntologyCache extends GenericCache<ReadOntology> {
requestedEntityDefs.classes[resourceClassIri]
= mainOnto.classes[resourceClassIri];

mainOnto.classes[resourceClassIri].propertiesList.forEach(
// filter out non Knora properties
requestedEntityDefs.classes[resourceClassIri].propertiesList = requestedEntityDefs.classes[resourceClassIri].propertiesList.filter(
(hasProp: IHasProperty) => {
return OntologyConversionUtil.getOntologyIriFromEntityIri(hasProp.propertyIndex, this.knoraApiConfig).length === 1;
}
);

requestedEntityDefs.classes[resourceClassIri].propertiesList.forEach(
(prop: IHasProperty) => {

// prop could refer to entities in the ontology the requested resource class belongs to
// or to other ontologies the resource class has prop cardinalities for, e.g. knora api or another project ontology.
const fromOntoIri = OntologyConversionUtil.getOntologyIriFromEntityIri(prop.propertyIndex, this.knoraApiConfig);

// only handle Knora property definitions
if (fromOntoIri.length === 1) {

const fromOnto = ontosMap.get(fromOntoIri[0]);

if (fromOnto === undefined) throw new Error("Expected ontology not found");

requestedEntityDefs.properties[prop.propertyIndex] = fromOnto.properties[prop.propertyIndex];

}
Expand Down
7 changes: 7 additions & 0 deletions src/models/v2/resources/ResourcesConversionUtil.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PropertyMatchingRule } from "json2typescript/src/json2typescript/json-c
import { of } from "rxjs";
import { MockList } from "../../../../test/data/api/v2/mockList";
import { MockOntology } from "../../../../test/data/api/v2/mockOntology";
import { MockOntologyAssertions } from "../../../../test/data/api/v2/mock-ontology-assertions";
import { KnoraApiConfig } from "../../../knora-api-config";
import { KnoraApiConnection } from "../../../knora-api-connection";
import { ReadResource } from "./read/read-resource";
Expand Down Expand Up @@ -73,6 +74,12 @@ describe("ResourcesConversionUtil", () => {

expect(resSeq.resources.length).toEqual(1);

// make sure that mocked ontology cache works as expected
expect(resSeq.resources[0].entityInfo.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"].propertiesList.length).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.length);
expect(resSeq.resources[0].entityInfo.classes["http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"].propertiesList.map(prop => prop.propertyIndex).sort()).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.sort());
expect(Object.keys(resSeq.resources[0].entityInfo.properties).length).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.length);
expect(Object.keys(resSeq.resources[0].entityInfo.properties).sort()).toEqual(MockOntologyAssertions.propertyIndexesAnythingThing.sort());

expect(resSeq.resources[0].id).toEqual("http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw");
expect(resSeq.resources[0].type).toEqual("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing");
expect(resSeq.resources[0].label).toEqual("testding");
Expand Down
48 changes: 48 additions & 0 deletions test/data/api/v2/mock-ontology-assertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* This class contains assertions for the mocked ontologies
* so that the behave as the lib's ontology handling in production.
*/
export class MockOntologyAssertions {

static propertyIndexesAnythingThing = [
"http://api.knora.org/ontology/knora-api/v2#arkUrl",
"http://api.knora.org/ontology/knora-api/v2#attachedToProject",
"http://api.knora.org/ontology/knora-api/v2#attachedToUser",
"http://api.knora.org/ontology/knora-api/v2#creationDate",
"http://api.knora.org/ontology/knora-api/v2#deleteComment",
"http://api.knora.org/ontology/knora-api/v2#deleteDate",
"http://api.knora.org/ontology/knora-api/v2#deletedBy",
"http://api.knora.org/ontology/knora-api/v2#hasIncomingLinkValue",
"http://api.knora.org/ontology/knora-api/v2#hasPermissions",
"http://api.knora.org/ontology/knora-api/v2#hasStandoffLinkTo",
"http://api.knora.org/ontology/knora-api/v2#hasStandoffLinkToValue",
"http://api.knora.org/ontology/knora-api/v2#isDeleted",
"http://api.knora.org/ontology/knora-api/v2#lastModificationDate",
"http://api.knora.org/ontology/knora-api/v2#userHasPermission",
"http://api.knora.org/ontology/knora-api/v2#versionArkUrl",
"http://api.knora.org/ontology/knora-api/v2#versionDate",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasListItem",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherListItem",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThingValue",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasRichtext",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasText",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasDate",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasBoolean",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasUri",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasInterval",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasColor",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasGeometry",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasGeoname",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasThingDocument",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasThingDocumentValue",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasThingPicture",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasThingPictureValue",
"http://0.0.0.0:3333/ontology/0001/anything/v2#hasTimeStamp",
"http://0.0.0.0:3333/ontology/0001/anything/v2#isPartOfOtherThing",
"http://0.0.0.0:3333/ontology/0001/anything/v2#isPartOfOtherThingValue"
];

}
10 changes: 9 additions & 1 deletion test/data/api/v2/mockOntology.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { JsonConvert, OperationMode, ValueCheckingMode } from "json2typescript";
import { PropertyMatchingRule } from "json2typescript/src/json2typescript/json-convert-enums";
import { IResourceClassAndPropertyDefinitions } from "../../../../src/cache/OntologyCache";
import { Constants } from "../../../../src/models/v2/Constants";
import { IHasProperty } from "../../../../src/models/v2/ontologies/class-definition";
import { OntologyConversionUtil } from "../../../../src/models/v2/ontologies/OntologyConversionUtil";
import { ReadOntology } from "../../../../src/models/v2/ontologies/read-ontology";
import { ResourceClassDefinition } from "../../../../src/models/v2/ontologies/resource-class-definition";
Expand Down Expand Up @@ -87,7 +89,6 @@ export namespace MockOntology {
const knoraApiOntology: any = knoraApiOntologyExpanded;
const incunabulaOntology: any = incunabulaOntologyExpanded;


const knoraApiEntities = (knoraApiOntology as { [index: string]: object[] })["@graph"];
const anythingEntities = (anythingOntology as { [index: string]: object[] })["@graph"];
const incunabulaEntities = (incunabulaOntology as { [index: string]: object[] })["@graph"];
Expand All @@ -102,6 +103,13 @@ export namespace MockOntology {
entityMock.classes[resClass.id] = resClass;
});

entityMock.classes[resClassIri].propertiesList = entityMock.classes[resClassIri].propertiesList.filter(
(prop: IHasProperty) => {
// exclude rdfs:label
return prop.propertyIndex !== Constants.Label;
}
);

// properties of anything Thing
const props: string[]
= entityMock.classes[resClassIri].propertiesList.map(prop => prop.propertyIndex);
Expand Down