UNPKG

@knora/core

Version:
1,292 lines (1,266 loc) 85.9 kB
import { __decorate, __param, __metadata } from 'tslib'; import { InjectionToken, NgModule, Inject, ɵɵdefineInjectable, ɵɵinject, Injectable } from '@angular/core'; import { Constants, KnoraApiConnection, KnoraApiConfig } from '@knora/api'; import * as momentImported from 'moment'; import { JsonConvert, OperationMode, ValueCheckingMode } from 'json2typescript'; import { BehaviorSubject, from, Observable, of, forkJoin } from 'rxjs'; import { HttpHeaders, HttpClient } from '@angular/common/http'; import { throwError } from 'rxjs/internal/observable/throwError'; import { map, catchError, mergeMap } from 'rxjs/operators'; var KuiCoreModule_1; // config for knora-api-js-lib (@knora/api) const KnoraApiConfigToken = new InjectionToken('Knora api configuration'); // connection config for knora-api-js-lib (@knora/api) const KnoraApiConnectionToken = new InjectionToken('Knora api connection configuration'); // config for knora-ui (@knora/action, @knora/search, @knora/viewer) const KuiConfigToken = new InjectionToken('Main configuration for knora-ui modules'); let KuiCoreModule = KuiCoreModule_1 = class KuiCoreModule { static forRoot(kuiConfig) { // get the app environment configuration here // console.log('KuiCoreModule - forRoot - config: ', config); return { ngModule: KuiCoreModule_1, providers: [ { provide: KuiConfigToken, useValue: kuiConfig } ] }; } }; KuiCoreModule = KuiCoreModule_1 = __decorate([ NgModule({ imports: [], declarations: [], exports: [] }) ], KuiCoreModule); class KuiConfig { constructor(knora, app) { this.knora = knora; this.app = app; } } const moment = momentImported; let SessionService = class SessionService { constructor(knoraApiConnection, knoraApiConfig) { this.knoraApiConnection = knoraApiConnection; this.knoraApiConfig = knoraApiConfig; /** * max session time in milliseconds * default value (24h): 86400000 * */ this.MAX_SESSION_TIME = 86400000; // 1d = 24 * 60 * 60 * 1000 } /** * set the session by using the json web token (jwt) and the user object; * it will be used in the login process * * @param jwt * @param username */ setSession(jwt, identifier, identifierType) { let session; this.updateKnoraApiConnection(jwt); // get user information this.knoraApiConnection.admin.usersEndpoint.getUser(identifierType, identifier).subscribe((response) => { let sysAdmin = false; const projectAdmin = []; // get permission inforamation: a) is user sysadmin? b) get list of project iris where user is project admin const groupsPerProjectKeys = Object.keys(response.body.user.permissions.groupsPerProject); for (const key of groupsPerProjectKeys) { if (key === Constants.SystemProjectIRI) { sysAdmin = response.body.user.permissions.groupsPerProject[key].indexOf(Constants.SystemAdminGroupIRI) > -1; } if (response.body.user.permissions.groupsPerProject[key].indexOf(Constants.ProjectAdminGroupIRI) > -1) { projectAdmin.push(key); } } // store session information in browser's localstorage session = { id: this.setTimestamp(), user: { name: response.body.user.username, jwt: jwt, lang: response.body.user.lang, sysAdmin: sysAdmin, projectAdmin: projectAdmin } }; // update localStorage localStorage.setItem('session', JSON.stringify(session)); }, (error) => { localStorage.removeItem('session'); console.error(error); }); } /** * Validate intern session and check knora api credentials if necessary. * If a json web token exists, it doesn't mean, that the knora api credentials are still valid. * * @returns boolean */ validateSession() { // mix of checks with session.validation and this.authenticate const session = JSON.parse(localStorage.getItem('session')); const tsNow = this.setTimestamp(); if (session) { this.updateKnoraApiConnection(session.user.jwt); // check if the session is still valid: if (session.id + this.MAX_SESSION_TIME <= tsNow) { // the internal (knora-ui) session has expired // check if the api credentails are still valid // console.error('session is not valid; check knora api credentials'); this.knoraApiConnection.v2.auth.checkCredentials().subscribe((response) => { // the knora api credentials are still valid // console.log('knora api credentials', response); // refresh the jwt in @knora/api this.updateKnoraApiConnection(session.user.jwt); // update the session.id session.id = tsNow; localStorage.setItem('session', JSON.stringify(session)); // console.log('knora api credentials are valid; return', true); return true; }, (error) => { // a user is not authenticated anymore! // console.error('session.service -- validateSession -- authenticate: the session expired on API side'); this.destroySession(); // console.warn('knora api credentials are not valid; return', false); return false; }); } else { // the internal (knora-ui) session is still valid // console.log('session is valid; return', true); return true; } } else { // no session found; update knora api connection with empty jwt this.updateKnoraApiConnection(); // console.warn('session is not valid; return', false); return false; } } /** * update the session storage * @param jwt * @param username * * @returns boolean */ updateSession(jwt, username) { if (jwt && username) { this.setSession(jwt, username, 'username'); return true; } else { return false; } } /** * Destroy session by removing the session from local storage * */ destroySession() { localStorage.removeItem('session'); } /** * Update the knora-api-config and knora-api-connection of @knora/api * * @param {string} jwt? */ updateKnoraApiConnection(jwt) { this.knoraApiConfig.jsonWebToken = (jwt ? jwt : ''); this.knoraApiConnection = new KnoraApiConnection(this.knoraApiConfig); } /** * Convert a timestamp in a number * @returns number */ setTimestamp() { return (moment().add(0, 'second')).valueOf(); } }; SessionService.ctorParameters = () => [ { type: KnoraApiConnection, decorators: [{ type: Inject, args: [KnoraApiConnectionToken,] }] }, { type: KnoraApiConfig, decorators: [{ type: Inject, args: [KnoraApiConfigToken,] }] } ]; SessionService.ngInjectableDef = ɵɵdefineInjectable({ factory: function SessionService_Factory() { return new SessionService(ɵɵinject(KnoraApiConnectionToken), ɵɵinject(KnoraApiConfigToken)); }, token: SessionService, providedIn: "root" }); SessionService = __decorate([ Injectable({ providedIn: 'root' }), __param(0, Inject(KnoraApiConnectionToken)), __param(1, Inject(KnoraApiConfigToken)), __metadata("design:paramtypes", [KnoraApiConnection, KnoraApiConfig]) ], SessionService); /** * @deprecated Use ApiResponseData from `@knora/api` (github:dasch-swiss/knora-api-js-lib) instead * Result class used as API url response in ApiService */ class ApiServiceResult { constructor() { /** * Status number */ this.status = 0; /** * Status text */ this.statusText = ''; /** * API url */ this.url = ''; } /** * Gets the result body as instance of classObject. * @param classObject * @returns {any} * @throws */ getBody(classObject) { // console.log(this.body); return ApiServiceResult.jsonConvert.deserialize(this.body, classObject); } } ApiServiceResult.jsonConvert = new JsonConvert(OperationMode.ENABLE, ValueCheckingMode.ALLOW_NULL); /** * @deprecated Use ApiResponseError from `@knora/api` (github:dasch-swiss/knora-api-js-lib) instead * * Error class used as API response in ApiService */ class ApiServiceError { constructor() { /** * Status number */ this.status = 0; /** * Status text */ this.statusText = ''; /** * API url */ this.url = ''; /** * Additional error info */ this.errorInfo = ''; } } class KnoraConstants { } // The following version of Knora is needed to work properly with this module KnoraConstants.KnoraVersion = '12.0.0'; KnoraConstants.KnoraApi = 'http://api.knora.org/ontology/knora-api'; KnoraConstants.PathSeparator = '#'; // Simple KnoraConstants.KnoraApiV2SimplePath = KnoraConstants.KnoraApi + '/simple/v2' + KnoraConstants.PathSeparator; KnoraConstants.resourceSimple = KnoraConstants.KnoraApiV2SimplePath + 'Resource'; KnoraConstants.dateSimple = KnoraConstants.KnoraApiV2SimplePath + 'Date'; KnoraConstants.intervalSimple = KnoraConstants.KnoraApiV2SimplePath + 'Interval'; KnoraConstants.geomSimple = KnoraConstants.KnoraApiV2SimplePath + 'Geom'; KnoraConstants.colorSimple = KnoraConstants.KnoraApiV2SimplePath + 'Color'; KnoraConstants.geonameSimple = KnoraConstants.KnoraApiV2SimplePath + 'Geoname'; KnoraConstants.fileSimple = KnoraConstants.KnoraApiV2SimplePath + 'File'; KnoraConstants.listNodeSimple = KnoraConstants.KnoraApiV2SimplePath + 'ListNode'; // comparison operators --> TODO: should be moved to search-module!? KnoraConstants.EqualsComparisonOperator = '='; KnoraConstants.EqualsComparisonLabel = 'is equal to'; KnoraConstants.NotEqualsComparisonOperator = '!='; KnoraConstants.NotEqualsComparisonLabel = 'is not equal to'; KnoraConstants.GreaterThanComparisonOperator = '>'; KnoraConstants.GreaterThanComparisonLabel = 'is greater than'; KnoraConstants.GreaterThanEqualsComparisonOperator = '>='; KnoraConstants.GreaterThanEqualsComparisonLabel = 'is greater than equals to'; KnoraConstants.LessThanComparisonOperator = '<'; KnoraConstants.LessThanComparisonLabel = 'is less than'; KnoraConstants.LessThanEqualsComparisonOperator = '<='; KnoraConstants.LessThanQualsComparisonLabel = 'is less than equals to'; KnoraConstants.ExistsComparisonOperator = 'E'; KnoraConstants.ExistsComparisonLabel = 'exists'; KnoraConstants.LikeComparisonOperator = 'regex'; KnoraConstants.LikeComparisonLabel = 'is like'; KnoraConstants.MatchComparisonOperator = 'contains'; KnoraConstants.MatchComparisonLabel = 'matches'; KnoraConstants.MatchFunction = KnoraConstants.KnoraApi + '/v2' + KnoraConstants.PathSeparator + 'match'; // Knora Schema var KnoraSchema; (function (KnoraSchema) { KnoraSchema[KnoraSchema["complex"] = 0] = "complex"; KnoraSchema[KnoraSchema["simple"] = 1] = "simple"; })(KnoraSchema || (KnoraSchema = {})); /** * @deprecated since v10.0.0 * * Will be replaced by `@knora/api` (github:knora-api-js-lib) * * Represents the parameters of an extended search. */ class ExtendedSearchParams { /** * * @param generateGravsearch a function that generates a Gravsearch query. * * The function takes the offset * as a parameter and returns a Gravsearch query string. * Returns false if not set correctly (init state). */ constructor(generateGravsearch) { this.generateGravsearch = generateGravsearch; } } let SearchParamsService = /** * @deprecated since v10.0.0 * * Will be replaced by `@knora/api` (github:knora-api-js-lib) * Temporarily stores the parameters of an extended search. */ class SearchParamsService { constructor() { // init with a dummy function that returns false // if the application is reloaded, this will be returned this._currentSearchParams = new BehaviorSubject(new ExtendedSearchParams((offset) => false)); } /** * Updates the parameters of an extended search. * * @param {ExtendedSearchParams} searchParams * @returns void */ changeSearchParamsMsg(searchParams) { this._currentSearchParams.next(searchParams); } /** * Gets the search params of an extended search. * * @returns ExtendedSearchParams - search parameters */ getSearchParams() { return this._currentSearchParams.getValue(); } }; SearchParamsService.ngInjectableDef = ɵɵdefineInjectable({ factory: function SearchParamsService_Factory() { return new SearchParamsService(); }, token: SearchParamsService, providedIn: "root" }); SearchParamsService = __decorate([ Injectable({ providedIn: 'root' }) /** * @deprecated since v10.0.0 * * Will be replaced by `@knora/api` (github:knora-api-js-lib) * Temporarily stores the parameters of an extended search. */ , __metadata("design:paramtypes", []) ], SearchParamsService); var GravsearchGenerationService_1; /** * @deprecated Use new service from `@knora/api` (github:dasch-swiss/knora-api-js-lib) instead * * Create GravSearch queries from provided parameters. */ let GravsearchGenerationService = GravsearchGenerationService_1 = class GravsearchGenerationService { constructor(_searchParamsService) { this._searchParamsService = _searchParamsService; } /** * @deprecated since v10.0.0 * * Will be replaced by `@knora/api` (github:knora-api-js-lib) * * Generates a Gravsearch query from the provided arguments. * * @param {PropertyWithValue[]} properties the properties specified by the user. * @param {string} [mainResourceClassOption] the class of the main resource, if specified. * @param {number} offset the offset to be used (nth page of results). * @returns string - a KnarQL query string. */ createGravsearchQuery(properties, mainResourceClassOption, offset = 0) { // class restriction for the resource searched for let mainResourceClass = ''; // if given, create the class restriction for the main resource if (mainResourceClassOption !== undefined) { mainResourceClass = `?mainRes a <${mainResourceClassOption}> .`; } // criteria for the order by statement const orderByCriteria = []; // statements to be returned in query results const returnStatements = []; // loop over given properties and create statements and filters from them const props = properties.map((propWithVal, index) => { // represents the object of a statement let propValue; if (!propWithVal.property.isLinkProperty || propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Exists') { // it is not a linking property, create a variable for the value (to be used by a subsequent FILTER) // OR the comparison operator Exists is used in which case we do not need to specify the object any further propValue = `?propVal${index}`; } else { // it is a linking property and the comparison operator is not Exists, use its IRI propValue = propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex); } // generate statement let statement = `?mainRes <${propWithVal.property.id}> ${propValue} .`; // check if it is a linking property that has to be wrapped in a FILTER NOT EXISTS (comparison operator NOT_EQUALS) to negate it if (propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { // do not include statement in results, because the query checks for the absence of this statement statement = `FILTER NOT EXISTS { ${statement} }`; } else { // TODO: check if statement should be returned returned in results (Boolean flag from checkbox) returnStatements.push(statement); statement = ` ${statement} `; } // generate restricting expression (e.g., a FILTER) if comparison operator is not Exists let restriction = ''; // only create a FILTER if the comparison operator is not EXISTS and it is not a linking property if (!propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() !== 'Exists') { // generate variable for value literal const propValueLiteral = `${propValue}Literal`; if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Like') { // generate statement to value literal restriction = `${propValue} <${GravsearchGenerationService_1.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; // use regex function for LIKE restriction += `FILTER regex(${propValueLiteral}, ${propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex)}, "i")`; } else if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Match') { // generate statement to value literal restriction = `${propValue} <${GravsearchGenerationService_1.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; // use contains function for MATCH restriction += `FILTER <${KnoraConstants.MatchFunction}>(${propValueLiteral}, ${propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex)})`; } else if (propWithVal.property.objectType === Constants.DateValue) { // handle date property restriction = `FILTER(knora-api:toSimpleDate(${propValue}) ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex)})`; } else if (propWithVal.property.objectType === Constants.ListValue) { // handle list node restriction = `${propValue} <${GravsearchGenerationService_1.complexTypeToProp[propWithVal.property.objectType]}> ${propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex)}` + '\n'; // check for comparison operator "not equals" if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { restriction = `FILTER NOT EXISTS { ${restriction} }`; } } else { // generate statement to value literal restriction = `${propValue} <${GravsearchGenerationService_1.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; // generate filter expression restriction += `FILTER(${propValueLiteral} ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql(KnoraSchema.complex)})`; } } // check if current value is a sort criterion if (propWithVal.isSortCriterion) orderByCriteria.push(propValue); return `${statement} ${restriction} `; }); let orderByStatement = ''; if (orderByCriteria.length > 0) { orderByStatement = ` ORDER BY ${orderByCriteria.join(' ')} `; } // template of the Gravsearch query with dynamic components const gravsearchTemplate = ` PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#> CONSTRUCT { ?mainRes knora-api:isMainResource true . ${returnStatements.join('\n')} } WHERE { ?mainRes a knora-api:Resource . ${mainResourceClass} ${props.join('')} } ${orderByStatement}`; // offset component of the Gravsearch query const offsetTemplate = ` OFFSET ${offset} `; // function that generates the same KnarQL query with the given offset const generateGravsearchQueryWithCustomOffset = (localOffset) => { const offsetCustomTemplate = ` OFFSET ${localOffset} `; return gravsearchTemplate + offsetCustomTemplate; }; if (offset === 0) { // store the function so another KnarQL query can be created with an increased offset this._searchParamsService.changeSearchParamsMsg(new ExtendedSearchParams(generateGravsearchQueryWithCustomOffset)); } return gravsearchTemplate + offsetTemplate; } }; /** * @ignore * * Map of complex knora-api value types to simple ones. * Use computed property name: http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer. */ GravsearchGenerationService.typeConversionComplexToSimple = { 'http://api.knora.org/ontology/knora-api/v2#IntValue': Constants.XsdInteger, 'http://api.knora.org/ontology/knora-api/v2#DecimalValue': Constants.XsdDecimal, 'http://api.knora.org/ontology/knora-api/v2#BooleanValue': Constants.XsdBoolean, 'http://api.knora.org/ontology/knora-api/v2#TextValue': Constants.XsdString, 'http://api.knora.org/ontology/knora-api/v2#UriValue': Constants.XsdAnyUri, 'http://api.knora.org/ontology/knora-api/v2#DateValue': KnoraConstants.dateSimple, 'http://api.knora.org/ontology/knora-api/v2#IntervalValue': KnoraConstants.intervalSimple, 'http://api.knora.org/ontology/knora-api/v2#GeomValue': KnoraConstants.geomSimple, 'http://api.knora.org/ontology/knora-api/v2#ColorValue': KnoraConstants.colorSimple, 'http://api.knora.org/ontology/knora-api/v2#GeonameValue': KnoraConstants.geonameSimple, 'http://api.knora.org/ontology/knora-api/v2#StillImageFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#FileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#MovingImageFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#DDDFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#AudioFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#DocumentFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#TextFileValue': KnoraConstants.fileSimple, 'http://api.knora.org/ontology/knora-api/v2#ListValue': KnoraConstants.listNodeSimple }; GravsearchGenerationService.complexTypeToProp = { 'http://api.knora.org/ontology/knora-api/v2#IntValue': Constants.IntValueAsInt, 'http://api.knora.org/ontology/knora-api/v2#DecimalValue': Constants.DecimalValueAsDecimal, 'http://api.knora.org/ontology/knora-api/v2#BooleanValue': Constants.BooleanValueAsBoolean, 'http://api.knora.org/ontology/knora-api/v2#TextValue': Constants.ValueAsString, 'http://api.knora.org/ontology/knora-api/v2#UriValue': Constants.UriValueAsUri, 'http://api.knora.org/ontology/knora-api/v2#ListValue': Constants.ListValueAsListNode }; GravsearchGenerationService.ctorParameters = () => [ { type: SearchParamsService } ]; GravsearchGenerationService.ngInjectableDef = ɵɵdefineInjectable({ factory: function GravsearchGenerationService_Factory() { return new GravsearchGenerationService(ɵɵinject(SearchParamsService)); }, token: GravsearchGenerationService, providedIn: "root" }); GravsearchGenerationService = GravsearchGenerationService_1 = __decorate([ Injectable({ providedIn: 'root' }), __metadata("design:paramtypes", [SearchParamsService]) ], GravsearchGenerationService); class Equals { constructor() { this.type = KnoraConstants.EqualsComparisonOperator; this.label = KnoraConstants.EqualsComparisonLabel; } getClassName() { return 'Equals'; } } class NotEquals { constructor() { this.type = KnoraConstants.NotEqualsComparisonOperator; this.label = KnoraConstants.NotEqualsComparisonLabel; } getClassName() { return 'NotEquals'; } } class GreaterThanEquals { constructor() { this.type = KnoraConstants.GreaterThanEqualsComparisonOperator; this.label = KnoraConstants.GreaterThanEqualsComparisonLabel; } getClassName() { return 'GreaterThanEquals'; } } class GreaterThan { constructor() { this.type = KnoraConstants.GreaterThanComparisonOperator; this.label = KnoraConstants.GreaterThanComparisonLabel; } getClassName() { return 'GreaterThan'; } } class LessThan { constructor() { this.type = KnoraConstants.LessThanComparisonOperator; this.label = KnoraConstants.LessThanComparisonLabel; } getClassName() { return 'LessThan'; } } class LessThanEquals { constructor() { this.type = KnoraConstants.LessThanEqualsComparisonOperator; this.label = KnoraConstants.LessThanQualsComparisonLabel; } getClassName() { return 'LessThanEquals'; } } class Exists { constructor() { this.type = KnoraConstants.ExistsComparisonOperator; this.label = KnoraConstants.ExistsComparisonLabel; } getClassName() { return 'Exists'; } } class Like { constructor() { this.type = KnoraConstants.LikeComparisonOperator; this.label = KnoraConstants.LikeComparisonLabel; } getClassName() { return 'Like'; } } class Match { constructor() { this.type = KnoraConstants.MatchComparisonOperator; this.label = KnoraConstants.MatchComparisonLabel; } getClassName() { return 'Match'; } } /** * Combination of a comparison operator and a value literal or an IRI. * In case the comparison operator is 'Exists', no value is given. */ class ComparisonOperatorAndValue { constructor(comparisonOperator, value) { this.comparisonOperator = comparisonOperator; this.value = value; } } /** * Represents a property's value as a literal with the indication of its type. */ class ValueLiteral { /** * Constructs a [ValueLiteral]. * * @param {string} value the literal representation of the value. * @param {string} type the type of the value (making use of xsd). */ constructor(value, type) { this.value = value; this.type = type; } /** * Creates a type annotated value literal to be used in a SPARQL query. * * @param schema indicates the Knora schema to be used. * @returns {string} */ toSparql(schema) { let literalType; // check if a Knora schema conversion is necessary, e.g., knora-api:dateValue (complex) to knora-api:date (simple). // xsd types will remain unchanged if (schema === KnoraSchema.simple && GravsearchGenerationService.typeConversionComplexToSimple[this.type] !== undefined) { // convert to simple schema literalType = GravsearchGenerationService.typeConversionComplexToSimple[this.type]; } else { // do not convert literalType = this.type; } return `"${this.value}"^^<${literalType}>`; } } /** * Represents an IRI. */ class IRI { /** * Constructs an [IRI]. * * @param {string} iri the IRI of a resource instance. */ constructor(iri) { this.iri = iri; } /** * Creates a SPARQL representation of the IRI. * * @param schema indicates the Knora schema to be used. * @returns {string} */ toSparql(schema) { // this is an instance Iri and does not have to be converted. return `<${this.iri}>`; } } /** * Represents a property, the specified comparison operator, and value. */ class PropertyWithValue { /** * Constructs a [PropertyWithValue]. * * @param {Property} property the specified property. * @param {ComparisonOperatorAndValue} valueLiteral the specified comparison operator and value. * @param isSortCriterion indicates if the property is used as a sort criterion. */ constructor(property, valueLiteral, isSortCriterion) { this.property = property; this.valueLiteral = valueLiteral; this.isSortCriterion = isSortCriterion; } } /** * Collection of useful utility functions. */ // @dynamic class Utils { /** * Given a Knora entity IRI, gets the ontology Iri. * * @param {string} entityIri an entity Iri. * @return {string} the ontology IRI */ static getOntologyIriFromEntityIri(entityIri) { // split class Iri on "#" const segments = entityIri.split(KnoraConstants.PathSeparator); if (segments.length !== 2) console.error(`Error: ${entityIri} is not a valid entity IRI.`); return segments[0]; } /** * Converts a complex knora-api entity Iri to a knora-api simple entity Iri. * * @param {string} complexEntityIri * @returns {string} */ static convertComplexKnoraApiEntityIritoSimple(complexEntityIri) { // split entity Iri on "#" const segments = complexEntityIri.split('v2' + KnoraConstants.PathSeparator); if (segments.length !== 2) console.error(`Error: ${complexEntityIri} is not a valid entity IRI.`); // add 'simple' to base path return segments[0] + 'simple/v2' + KnoraConstants.PathSeparator + segments[1]; } } /** * A regex to validate Email address. * * @type {RegExp} */ Utils.RegexEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; /** * A regex to validate Username. * * @type {RegExp} */ Utils.RegexUsername = /^[a-zA-Z0-9]+$/; /** * A regex to validate URLs. * * @type {RegExp} */ Utils.RegexUrl = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/.*)?$/i; /** * A regex to validate Passwords * * @type {RegExp} */ Utils.RegexPassword = /^(?=.*\d)(?=.*[a-zA-Z]).{8,}$/i; /** * A regex to validate Hexadecimal values * * @type {RegExp} */ Utils.RegexHex = /^[0-9A-Fa-f]+$/; /** * A regex to validate shortname in projects * * @type {RegExp} */ Utils.RegexShortname = /^[a-zA-Z]+\S*$/; /** * Lambda function eliminating duplicates in a collection to be passed to [[filter]]. * * @param elem element of an Array that is currently being looked at. * @param index current elements index. * @param self reference to the whole Array. * @returns {boolean} true if the same element does not already exist in the Array. */ Utils.filterOutDuplicates = (elem, index, self) => { // https://stackoverflow.com/questions/16747798/delete-duplicate-elements-from-an-array // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter?v=example // returns true if the element's index equals the index of the leftmost element // -> this means that there is no identical element before this index, hence it is not a duplicate // for all other elements, false is returned return index === self.indexOf(elem); }; /** * Represents a region. * Contains a reference to the resource representing the region and its geometries. */ class Region { /** * * @param {ReadResource} regionResource a resource of type Region */ constructor(regionResource) { this.regionResource = regionResource; } /** * Get all geometry information belonging to this region. * * @returns {ReadGeomValue[]} */ getGeometries() { return this.regionResource.properties[Constants.HasGeometry]; } } /** * @deprecated since v 10.0.0: replaced by knora-api-js-lib * */ const jsonld = require('jsonld'); const semver = require('semver'); let ApiService = class ApiService { constructor(http, knoraApiConfig) { this.http = http; this.knoraApiConfig = knoraApiConfig; // if is loading, set it true; // it can be used in components // for progress loader element this.loading = false; } /** * @deprecated since v9.5.0 * * GET * * @param {string} path the URL for the GET request. * @param {HttpParams} params the parameters for the GET request. * @returns Observable of any */ httpGet(path, params) { this.loading = true; const headers = this.setHeaders(); return this.http.get(this.knoraApiConfig.apiUrl + path, { headers: headers, observe: 'response', params: params }).pipe(map((response) => { this.loading = false; const result = new ApiServiceResult(); result.header = { 'server': response.headers.get('Server') }; this.compareVersion(response.headers.get('Server')); result.status = response.status; result.statusText = response.statusText; result.url = path; result.body = response.body; return result; }), catchError((error) => { this.loading = false; return this.handleRequestError(error); })); } /** * @deprecated since v9.5.0 * * Processes JSON-LD returned by Knora. * Expands Iris and creates an empty context object. * * @param {ApiServiceResult} resourceResponse */ processJSONLD(resourceResponse) { const resPromises = jsonld.promises; // compact JSON-LD using an empty context: expands all Iris const resPromise = resPromises.compact(resourceResponse.body, {}); // convert promise to Observable and return it // https://www.learnrxjs.io/operators/creation/frompromise.html return from(resPromise); } /** * @deprecated since v9.5.0 * * POST * * @param {string} path * @param {any} body * @returns Observable of any */ httpPost(path, body) { this.loading = true; const headers = this.setHeaders(); return this.http.post(this.knoraApiConfig.apiUrl + path, body, { headers: headers, observe: 'response' }).pipe(map((response) => { this.loading = false; const result = new ApiServiceResult(); result.header = { 'server': response.headers.get('Server') }; this.compareVersion(result.header.server); result.status = response.status; result.statusText = response.statusText; result.url = path; result.body = response.body; return result; }), catchError((error) => { this.loading = false; // console.error(error); return this.handleRequestError(error); })); } /** * @deprecated since v9.5.0 * * PUT * * @param {string} path * @param {any} body * @returns Observable of any */ httpPut(path, body) { this.loading = true; const headers = this.setHeaders(); return this.http.put(this.knoraApiConfig.apiUrl + path, body, { headers: headers, observe: 'response' }).pipe(map((response) => { this.loading = false; // console.log(response); const result = new ApiServiceResult(); result.header = { 'server': response.headers.get('Server') }; this.compareVersion(result.header.server); result.status = response.status; result.statusText = response.statusText; result.url = path; result.body = response.body; return result; }), catchError((error) => { this.loading = false; // console.error(error); return this.handleRequestError(error); })); } /** * @deprecated since v9.5.0 * * DELETE * * @param {string} path * @returns Observable of any */ httpDelete(path) { this.loading = true; const headers = this.setHeaders(); return this.http.delete(this.knoraApiConfig.apiUrl + path, { headers: headers, observe: 'response' }).pipe(map((response) => { this.loading = false; // console.log(response); const result = new ApiServiceResult(); result.header = { 'server': response.headers.get('Server') }; this.compareVersion(result.header.server); result.status = response.status; result.statusText = response.statusText; result.url = path; result.body = response.body; return result; }), catchError((error) => { this.loading = false; // console.error(error); return this.handleRequestError(error); })); } /** * handle request error in case of server error * * @param {HttpErrorResponse} error * @returns Observable of ApiServiceError */ handleRequestError(error) { // console.error(error); const serviceError = new ApiServiceError(); serviceError.header = { 'server': error.headers.get('Server') }; serviceError.status = error.status; serviceError.statusText = error.statusText; serviceError.errorInfo = error.message; serviceError.url = error.url; return throwError(serviceError); } /** * handle json error in case of type error in json response (json2typescript) * * @param {any} error * @returns Observable of ApiServiceError */ handleJsonError(error) { if (error instanceof ApiServiceError) return throwError(error); const serviceError = new ApiServiceError(); serviceError.header = { 'server': error.headers.get('Server') }; serviceError.status = -1; serviceError.statusText = 'Invalid JSON'; serviceError.errorInfo = error; serviceError.url = ''; return throwError(serviceError); } /** * @deprecated since v9.5.0 * * @param {string} server */ compareVersion(server) { // expected knora api version const expected = KnoraConstants.KnoraVersion; // existing knora api version if (server) { const versions = server.split(' '); const existing = versions[0].split('/')[1]; // compare the two versions: expected vs existing if (semver.diff(existing, expected) === 'major') { console.warn('The version of the @knora/core module works with Knora v' + expected + ', but you are using it with Knora v' + existing); } } else { // console.warn('No server information from headers response'); } } /** * Set headers to authorise http requests * * This method was replaced by interceptor. But since the implementation * of new knora-api-js-lib services the interceptor is redundant. * As long we not all services are replaced by knora-api-js-lib * we have to use this setHeaders "hack" for the two services left. * * @returns HttpHeaders */ setHeaders() { const session = JSON.parse(localStorage.getItem('session')); if (session && session !== null) { return new HttpHeaders({ 'Authorization': `Bearer ${session.user.jwt}` }); } else { return new HttpHeaders(); } } }; ApiService.ctorParameters = () => [ { type: HttpClient }, { type: KnoraApiConfig, decorators: [{ type: Inject, args: [KnoraApiConfigToken,] }] } ]; ApiService.ngInjectableDef = ɵɵdefineInjectable({ factory: function ApiService_Factory() { return new ApiService(ɵɵinject(HttpClient), ɵɵinject(KnoraApiConfigToken)); }, token: ApiService, providedIn: "root" }); ApiService = __decorate([ Injectable({ providedIn: 'root', }), __param(1, Inject(KnoraApiConfigToken)), __metadata("design:paramtypes", [HttpClient, KnoraApiConfig]) ], ApiService); /** * @deprecated since v10.0.0 * * Will be replaced by `@knora/api` (github:knora-api-js-lib) * Requests ontology information from Knora. */ let OntologyService = class OntologyService extends ApiService { // ------------------------------------------------------------------------ // GET list of ontologies // ------------------------------------------------------------------------ /** * @deprecated: Use **getAllOntologies()** instead * * Requests the metadata about all existing ontologies from Knora's ontologies route. * * @returns Observable<ApiServiceResult> - the metadata of all ontologies. */ getOntologiesMetadata() { return this.httpGet('/v2/ontologies/metadata'); } /** * Requests the metadata about all existing ontologies from Knora's ontologies route. * * @returns Observable<ApiServiceResult> - the metadata of all ontologies. */ getAllOntologies() { return this.httpGet('/v2/ontologies/metadata'); } /** * Requests the ontologies of a specific project * * @param projectIri * @returns Observable<ApiServiceResult> - the metadata of project ontologies. */ getProjectOntologies(projectIri) { return this.httpGet('/v2/ontologies/metadata/' + encodeURIComponent(projectIri)); } // ------------------------------------------------------------------------ // GET ontology // ------------------------------------------------------------------------ /** * Requests all entity definitions for the given ontologies from Knora's ontologies route. * * @param {string} ontologyIri the Iris of the named graphs whose resource classes are to be returned. * @returns Observable<ApiServiceResult> - the requested ontology. */ getAllEntityDefinitionsForOntologies(ontologyIri) { return this.httpGet('/v2/ontologies/allentities/' + encodeURIComponent(ontologyIri)); } /** * Requests information about the given resource classes from Knora's ontologies route. * * @param {string[]} resourceClassIris the Iris of the resource classes to be queried. * @returns Observable<ApiServiceResult> - the requested resource class definitions. */ getResourceClasses(resourceClassIris) { if (resourceClassIris.length === 0) { // no resource class Iris are given to query for, return a failed Observer return Observable.create(observer => observer.error('No resource class Iris given for call of OntologyService.getResourceClasses')); } let resClassUriEnc = ''; resourceClassIris.forEach(function (resClassIri) { resClassUriEnc = resClassUriEnc + '/' + encodeURIComponent(resClassIri.toString()); }); return this.httpGet('/v2/ontologies/classes' + resClassUriEnc); } /** * Requests properties from Knora's ontologies route. * * @param {string[]} propertyIris the Iris of the properties to be queried. * @returns Observable<ApiServiceResult> - the requested properties. */ getProperties(propertyIris) { if (propertyIris.length === 0) { // no resource class Iris are given to query for, return a failed Observer return Observable.create(observer => observer.error('No property Iris given for call of OntologyService.getProperties')); } let propertiesUriEnc = ''; propertyIris.forEach(function (resClassIri) { propertiesUriEnc = propertiesUriEnc + '/' + encodeURIComponent(resClassIri.toString()); }); return this.httpGet('/v2/ontologies/properties' + propertiesUriEnc); } // ------------------------------------------------------------------------ // POST // ------------------------------------------------------------------------ /** * Create new ontology. * * @param {NewOntology} data Data contains: projectIri, name, label * @returns Observable<ApiServiceResult> incl. ontolog iri and knora-api:lastModificationDate */ createOntology(data) { const path = '/v2/ontologies'; const ontology = { 'knora-api:ontologyName': data.name, 'knora-api:attachedToProject': { '@id': data.projectIri, }, 'rdfs:label': data.label, '@context': { 'rdfs': Constants.Rdfs + Constants.Delimiter, 'knora-api': Constants.KnoraApiV2 + Constants.Delimiter } }; return this.httpPost(path, ontology).pipe(map((result) => result.body), catchError(this.handleJsonError)); } createResourceClass(data) { const path = '/v2/ontologies/classes'; // TODO: add the following values to parameter let onto_iri; let onto_name; let last_onto_date; const resourceClass = { '@id': onto_iri, '@type': 'owl:Ontology', 'knora-api:lastModificationDate': last_onto_date, '@graph': [{ '@id': onto_name + ':' + data.name, '@type': 'owl:Class', 'rdfs:label': data.labels, 'rdfs:comment': data.comments, 'rdfs:subClassOf': { '@id': data.subClassOf } }], '@context': { 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'knora-api': 'http://api.knora.org/ontology/knora-api/v2#', 'owl': 'http://www.w3.org/2002/07/owl#', 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 'xsd': 'http://www.w3.org/2001/XMLSchema#', onto_name: onto_iri + '#' } }; return this.httpPost(path, resourceClass).pipe(map((result) => result.body), catchError(this.handleJsonError)); } createProperty(data) { const path = '/v2/ontologies/properties'; // TODO: add the following values to parameter let onto_iri; let onto_name; let last_onto_date; const graph = []; for (const prop of data) { const prop_obj = { '@id': onto_name + ':' + prop.name, '@type': 'owl:ObjectProperty', 'rdfs:label': prop.labels, 'rdfs:comment': prop.comments, 'rdfs:subPropertyOf': prop.subPropertyOf, 'salsah-gui:guiElement': { '@id': prop.guiElement } }; graph.push(prop_obj); } const property = { '@id': onto_iri, '@type': 'owl:Ontology', 'knora-api:lastModificationDate': last_onto_date, '@graph': [ graph ], '@context': { 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'knora-api': 'http://api.knora.org/ontology/knora-api/v2#', 'salsah-gui': 'http://api.knora.org/ontology/salsah-gui/v2#', 'owl': 'http://www.w3.org/2002/07/owl#', 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 'xsd': 'http://www.w3.org/2001/XMLSchema#', onto_name: onto_iri + '#' } }; return this.httpPost(path, property).pipe(map((result) => result.body), catchError(this.handleJsonError)); } setCardinality(data) { const path = '/v2/ontologies/cardinalities'; // TODO: add the following values to parameter let class_iri; let prop_iri; let onto_iri; let onto_name; let last_onto_date; // TODO: find a way with typescript for the following python construct /* let switcher = { '1': ('owl:cardinality', 1), '0-1': ('owl:maxCardinality', 1), '0-n': ('owl:minCardinality', 0), '1-n': ('owl:minCardinality', 1) }; let occurrence: any = switcher.get(data.occurrence); */ const cardinality = { '@id': onto_iri, '@type': 'owl:Ontology', 'knora-api:lastModificationDate': last_onto_date, '@graph': [{ '@id': class_iri, '@type': 'owl:Class', 'rdfs:subClassOf': { '@type': 'owl:Restriction', // occurrence[0]: occurrence[1], 'owl:onProperty': { '@id': prop_iri } } }], '@context': { 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'knora-api': 'http://api.knora.org/ontology/knora-api/v2#', 'owl': 'http://www.w3.org/2002/07/owl#', 'rdfs': 'http://www.w3.org/2000/01/rdf-sc