import { Graph } from '@entryscape/rdfjson';
import Context from './Context.js';
import Entry from './Entry.js';
import EntryInfo from './EntryInfo.js';
import FileResource from './File.js';
import GraphResource from './Graph.js';
import Group from './Group.js';
import List from './List.js';
import Pipeline from './Pipeline.js';
import SearchList from './SearchList.js';
import StringResource from './String.js';
import types from './types.js';
import User from './User.js';
/**
* This module contains utility methods that encapsulates EntryStores REST layer from the rest
* of the code. It is intended to be used internally by the EntryStore.js API,
* not by application developers.
*
* Hence, **you should avoid using factory methods directly in application code as there are
* most probably other ways to achieve the same thing. Most likely by using method in
* {@link EntryStore}!**
*
* The utility methods are currently not visible as they are not documented yet.
* (The methods cannot be marked as private as they need to be used throughout the API.)
*
* @exports store/factory
* @namespace
*/
let sortObj = { sortBy: 'title', prio: 'List' };
let defaultLimit = 50;
/**
*
* @param entryURI
* @param entryStore
* @returns {*}
*/
const getContextForEntry = (entryURI, entryStore) => {
const baseURI = entryStore.getBaseURI();
const contextId = entryURI.substr(baseURI.length, entryURI.indexOf('/', baseURI.length)
- baseURI.length);
const contexts = entryStore.getCachedContextsIdx();
let context = contexts[contextId];
if (!context) {
context = new Context(`${baseURI}_contexts/entry/${contextId}`, baseURI
+ contextId, entryStore);
contexts[contextId] = context;
}
return context;
};
/**
*
* @param rights
*/
const transformRights = (rights) => {
const o = {};
const r = rights || [];
for (let n = 0; n < r.length; n++) {
o[r[n]] = true;
}
return o;
};
/**
*
* @param resObj
* @param data
*/
const fixNameAndDisabled = (resObj, data) => {
const { resource } = data;
// Special case of searches and similar when name is provided but not full resource.
if (resObj != null) {
if (resource && typeof resource.name === 'string') {
if (resObj instanceof User) {
resObj._data = resObj._data || {};
resObj._data.name = resource.name;
} else { // Context and Group
resObj._name = resource.name;
}
}
if (resObj instanceof User) {
resObj._data = resObj._data || {};
if (resource && typeof resource.disabled === 'boolean') {
resObj._data.disabled = resource.disabled;
}
}
}
};
/**
*
* @param entry
* @param data
* @param force
* @private
*/
const _updateOrCreateResource = (entry, data, force) => {
const _data = data || {};
let resource = entry.getResource(true);
const uri = entry.getURI();
const ruri = entry.getResourceURI();
const cruri = entry.getContext().getResourceURI();
const es = entry.getEntryStore();
const ei = entry.getEntryInfo();
if (!resource && ei.getEntryType() === types.ET_LOCAL
&& ei.getResourceType() === types.RT_INFORMATIONRESOURCE) {
switch (entry.getEntryInfo().getGraphType()) {
case types.GT_CONTEXT: // Synchronous resource, asynchronous methods.
// Dummy URL to find the right context.
resource = getContextForEntry(`${es.getBaseURI() + entry.getId()}/`,
entry.getEntryStore());
resource._update(_data);
break;
case types.GT_LIST: // Synchronous resource, asynchronous methods.
case types.GT_GROUP: // Synchronous resource, asynchronous methods.
if (entry.isGroup()) {
resource = new Group(uri, ruri, es);
} else {
resource = new List(uri, ruri, es);
}
if (_data.resource && _data.resource.children) {
resource._update(_data.resource, _data.resource.children.map(child =>
updateOrCreate(`${cruri}/entry/${child.entryId}`, child, es)));
}
break;
case types.GT_USER: // Asynchronous resource, synchronous getters.
if (force || _data.resource != null) {
resource = new User(uri, ruri, es, _data.resource || {});
}
break;
case types.GT_STRING:
if (force || _data.resource != null) {
resource = new StringResource(uri, ruri, es, _data.resource || '');
}
break;
case types.GT_GRAPH: // Sync or Async?
if (force || _data.resource != null) {
resource = new GraphResource(uri, ruri, es, _data.resource || {});
}
break;
case types.GT_PIPELINE: // Sync or Async?
if (force || _data.resource != null) {
resource = new Pipeline(uri, ruri, es, _data.resource || {});
}
break;
case types.GT_PIPELINERESULT: // If local, Pipelineresult resource is a file.
case types.GT_NONE: // Uploaded file.
resource = new FileResource(uri, ruri, es);
break;
default:
}
entry._resource = resource;
fixNameAndDisabled(resource, _data);
return;
}
if (resource == null || _data.resource == null) {
fixNameAndDisabled(resource, _data);
return;
}
if (resource._update) {
if (entry.isList() || entry.isGroup()) {
if (_data.resource && _data.resource.children) {
resource._update(_data.resource, _data.resource.children.map(child =>
updateOrCreate(`${cruri}/entry/${child.entryId}`,
child, entry.getEntryStore())));
}
} else {
resource._update(_data.resource);
}
}
};
/**
*
* @param entry
* @param data
* @returns {*}
* @private
*/
const _updateEntry = (entry, data) => {
entry._metadata = data.metadata ? new Graph(data.metadata) : null;
entry._cachedExternalMetadata = data['cached-external-metadata'] ? new Graph(data['cached-external-metadata']) : null;
entry._inferredMetadata = data.inferred ? new Graph(data.inferred) : null;
entry._extractedMetadata = data['extracted-metadata'] ? new Graph(data['extracted-metadata']) : null;
entry._relation = data.relations ? new Graph(data.relations) : new Graph();
entry._rights = transformRights(data.rights);
// Sometimes we get the name that is really part of the resource without getting the full
// resource, in this case we store this in the entryinfo.
if (data.name || data.alias || (data.resource && data.resource.name)) {
const ei = entry.getEntryInfo();
// ei._alias = data.alias;
ei._name = data.name || data.alias || data.resource.name;
}
// Sometimes we get the disabled state that is really part of the resource
// without getting the full resource, in this case we store this in the entryinfo.
if (data.disabled || (data.resource && data.resource.disabled)) {
const ei = entry.getEntryInfo();
ei._disabled = data.disabled || data.resource.disabled;
}
return entry;
};
/**
*
* @param entryStore
* @param contextEntryURI
* @return {Context}
*/
const getContext = (entryStore, contextEntryURI) => {
const baseURI = entryStore.getBaseURI();
const contextsBaseURI = `${baseURI}_contexts/entry/`;
const contextId = contextEntryURI.substr(contextsBaseURI.length);
const contexts = entryStore.getCachedContextsIdx();
let context = contexts[contextId];
if (!context) {
context = new Context(contextEntryURI, baseURI + contextId, entryStore);
contexts[contextId] = context;
}
return context;
};
/**
*
* @param entryStore
* @param entryURI
* @return {List}
*/
const getList = (entryStore, entryURI) => {
const cache = entryStore.getCache();
let entry = cache.get(entryURI);
if (!entry) { // If no entry is in cache, create an empty entry
// Assuming there is an info object... TODO check so not info_stub remains in rest layer.
const entryInfo = new EntryInfo(entryURI, new Graph(), entryStore);
const context = getContextForEntry(entryURI, entryStore);
entry = new Entry(context, entryInfo);
const resourceURI = entryURI.replace('/entry/', '/resource/');
entry._resource = new List(entryURI, resourceURI, entryStore);
cache.cache(entry, true); // Add to cache silently
entry.setRefreshNeeded(true); // Make sure it needs to be updated before accessed
}
// Returning only the list which has no reference to the entry isolates the entry from
// being accessed before refreshed.
return entry._resource;
};
/**
*
* @param entryURI
* @param data
* @param entryStore
* @return {Entry}
*/
const updateOrCreate = (entryURI, data, entryStore) => {
const cache = entryStore.getCache();
let entry = cache.get(entryURI);
if (entry) {
entry.getEntryInfo().setGraph(new Graph(data.info));
} else {
// Assuming there is an info object... TODO check so not info_stub remains in rest layer.
const entryInfo = new EntryInfo(entryURI, new Graph(data.info), entryStore);
const context = getContextForEntry(entryURI, entryStore);
entry = new Entry(context, entryInfo);
}
_updateEntry(entry, data);
_updateOrCreateResource(entry, data);
cache.cache(entry); // Add to or refresh the cache.
return entry;
};
/**
*
* @type {_updateOrCreateResource}
*/
const updateOrCreateResource = _updateOrCreateResource;
/**
*
* @param {Entry} entry
* @param {Object} data
*/
const update = (entry, data) => {
entry.getEntryInfo().setGraph(new Graph(data.info));
_updateOrCreateResource(entry, data);
_updateEntry(entry, data);
entry.getEntryStore().getCache().cache(entry); // Add to or refresh the cache.
};
/**
*
* @param entryStore
* @param query
* @return {SearchList}
*/
const createSearchList = (entryStore, query) => new SearchList(entryStore, query);
/**
*
* @param data
* @param list
* @param entryStore
* @return {Array.<Entry>}
*/
const extractSearchResults = (data, list, entryStore) => {
// Update or create all entries received
// TODO change rest api so offset is inside of resource.
data.resource.offset = data.resource.offset || data.offset;
// TODO change rest api so size is inside of resource.
data.resource.size = data.resource.size || data.results;
const baseURI = entryStore.getBaseURI();
const entries = data.resource.children.map(child => updateOrCreate(
`${baseURI + child.contextId}/entry/${child.entryId}`, child, entryStore));
list._update(data.resource, entries);
return entries;
};
/**
*
* @param entryURI
* @return {string}
*/
const getCachedExternalMetadataURI = entryURI => entryURI.replace('/entry/', '/cached-external-metadata/');
/**
* @deprecated in favor of {@link factory#getEntryId}
*/
const getId = uri => uri.substr(uri.lastIndexOf('/') + 1);
/**
*
* @param uri
* @param base
* @return {string|undefined}
*/
const getEntryId = (uri, base) => {
let _uri = uri;
if (base) {
_uri = _uri.substr(base.length - 1); // include the / before.
}
const res = _uri.match(/\/([^/]+)\/(entry|resource|metadata|relation)\/([^?/]+)(\?.*)?$/);
if (res) {
return res[3];
} else if (_uri.lastIndexOf('/') === 0) {
return _uri.substr(1);
} else if (!base) {
return _uri.substr(_uri.lastIndexOf('/') + 1);
}
return undefined;
};
/**
*
* @param uri
* @param base
* @return {string|undefined}
*/
const getContextId = (uri, base) => {
let _uri = uri;
if (base) {
_uri = _uri.substr(base.length - 1); // include the / before.
}
const res = _uri.match(/\/([^/]+)\/(entry|resource|metadata|relation)\/([^?/]+)(\?.*)?$/);
if (res) {
return res[1];
} else if (_uri.indexOf('/') === -1 || !base) {
return '_contexts';
}
return undefined;
};
/**
*
* @param entryStore
* @param uri
* @return {string}
*/
const getEntryURIFromURI = (entryStore, uri) => {
const base = entryStore.getBaseURI();
return `${base + getContextId(uri, base)}/entry/${getEntryId(uri, base)}`;
};
/**
*
* @param entryStore
* @param contextId
* @param entryId
* @return {string}
*/
const getEntryURI = (entryStore, contextId, entryId) => `${entryStore.getBaseURI()}${contextId}/entry/${entryId}`;
/**
*
* @param entryStore
* @param uri
* @return {string}
*/
const getMetadataURIFromURI = (entryStore, uri) => {
const base = entryStore.getBaseURI();
return `${base + getContextId(uri, base)}/metadata/${getEntryId(uri, base)}`;
};
/**
*
* @param entryStore
* @param contextId
* @param entryId
* @return {string}
*/
const getMetadataURI = (entryStore, contextId, entryId) =>
`${entryStore.getBaseURI()}${contextId}/entry/${entryId}`;
/**
*
* @param entryStore
* @param contextId
* @return {string}
*/
const getResourceBase = (entryStore, contextId) =>
`${entryStore.getBaseURI() + contextId}/resource/`;
/**
*
* @param entryStore
* @param contextId
* @param entryId
* @return {string}
*/
const getResourceURI = (entryStore, contextId, entryId) => {
if (contextId === '_contexts') {
return entryStore.getBaseURI() + entryId;
}
return `${entryStore.getBaseURI() + contextId}/resource/${entryId}`;
};
/**
*
* @param data
* @param context
* @return {string}
*/
const getURIFromCreated = (data, context) =>
`${context.getResourceURI()}/entry/${data.entryId}`;
/**
*
* @param entryURI
* @param params
* @return {string}
*/
const getEntryLoadURI = (entryURI, params) => {
const _params = params || {};
let strL = '';
if (_params.limit > 0 || _params.limit === -1) {
strL = `&limit=${_params.limit}`;
} else {
strL = `&limit=${defaultLimit}`;
}
const strO = _params.offset == null || _params.offset === 0 ? '' : `&offset=${_params.offset}`;
const sort = _params.sort == null ? sortObj : _params.sort;
let strSort = '';
let strDesc = '';
let strPrio = '';
if (sort != null) {
strSort = sort.sortBy == null ? '' : `&sort=${sort.sortBy}`;
strDesc = sort.descending === true ? '&order=desc' : '';
strPrio = sort.prio == null ? '' : `&prio=${sort.prio}`;
// TODO lang remains.
}
return `${entryURI}?includeAll${strL}${strO}${strSort}${strDesc}${strPrio}`;
};
/**
*
* @param prototypeEntry
* @param parentListEntry
* @return {string}
*/
const getEntryCreateURI = (prototypeEntry, parentListEntry) => {
let uri = `${prototypeEntry.getContext().getResourceURI()}?`;
if (prototypeEntry) {
const ei = prototypeEntry.getEntryInfo();
if (prototypeEntry.getSpecificId() != null) {
uri = `${uri}id=${prototypeEntry.getSpecificId()}&`;
}
if (prototypeEntry.isLink()) {
uri = `${uri}resource=${encodeURIComponent(prototypeEntry.getResourceURI())}&`;
}
if (prototypeEntry.isReference() || prototypeEntry.isLinkReference()) { // external metadata
uri = `${uri}resource=${encodeURIComponent(prototypeEntry.getResourceURI())}&`;
uri = `${uri}cached-external-metadata=${encodeURIComponent(ei.getExternalMetadataURI())}&`;
}
if (ei.getEntryType() !== types.ET_LOCAL) { // local, link, linkreference, reference
uri = `${uri}entrytype=${ei.getEntryType().toLowerCase()}&`;
}
// informationresource, namedresource
if (ei.getResourceType() !== types.RT_INFORMATIONRESOURCE) {
// TODO Bug in REST layer, should be resourcetype, is now informationresource innstead
uri = `${uri}informationresource=false&`;
}
if (ei.getGraphType() !== types.GT_NONE) {
uri = `${uri}graphtype=${ei.getGraphType().toLowerCase()}&`;
}
}
if (parentListEntry) {
uri = `${uri}list=${parentListEntry.getResourceURI()}&`;
}
return uri.slice(0, -1);
};
/**
*
* @param prototypeEntry
* @return {string}
*/
const getEntryCreatePostData = (prototypeEntry) => {
const postData = {};
let empty = true;
const md = prototypeEntry.getMetadata();
if (md != null && !md.isEmpty()) {
postData.metadata = md.exportRDFJSON();
empty = false;
}
const re = prototypeEntry.getResource(true);
if (re != null && re.getSource != null) {
postData.resource = re.getSource();
empty = false;
}
const ei = prototypeEntry.getEntryInfo().getGraph();
if (ei != null && !ei.isEmpty()) {
postData.info = ei.exportRDFJSON();
empty = false;
}
const cachedExternalMetadata = prototypeEntry.getCachedExternalMetadata();
if (cachedExternalMetadata != null && !cachedExternalMetadata.isEmpty()) {
postData['cached-external-metadata'] = cachedExternalMetadata.exportRDFJSON();
empty = false;
}
return empty ? '' : JSON.stringify(postData);
};
/**
*
* @param entry
* @param fromListEntry
* @param toListEntry
* @param baseURI
* @return {string}
*/
const getMoveURI = (entry, fromListEntry, toListEntry, baseURI) => {
const entryURI = entry.getURI().substr(baseURI.length); // Only send something like 3/entry/2
const furi = fromListEntry.getResourceURI().substr(baseURI.length);
return `${toListEntry.getResourceURI()}?moveEntry=${entryURI}&fromList=${furi}`;
};
/**
*
* @param baseURI
* @param uri
* @param formatHint
* @return {string}
*/
const getProxyURI = (baseURI, uri, formatHint) => {
let url = `${baseURI}proxy?url=${encodeURIComponent(uri)}`;
if (formatHint != null) {
url += `&fromFormat=${formatHint}`;
}
return url;
};
/**
*
* @param uri
* @return {string}
*/
const getPutFileURI = uri =>
`${uri + (uri.indexOf('?') < 0 ? '?' : '&')}method=put&textarea=true`;
/**
* @param sortObject
*/
const setSort = (sortObject) => {
sortObj = sortObject;
};
/**
* @return {{sortBy: string, prio: string}}
*/
const getSort = () => sortObj;
/**
*
* @return {number}
*/
const getDefaultLimit = () => defaultLimit;
/**
*
* @param limit
*/
const setDefaultLimit = (limit) => {
defaultLimit = limit;
};
export default {
getContext,
getList,
updateOrCreate,
updateOrCreateResource,
update,
createSearchList,
extractSearchResults,
getCachedExternalMetadataURI,
getId,
getEntryId,
getContextId,
getEntryURIFromURI,
getEntryURI,
getMetadataURIFromURI,
getMetadataURI,
getResourceBase,
getResourceURI,
getURIFromCreated,
getEntryLoadURI,
getEntryCreateURI,
getEntryCreatePostData,
getMoveURI,
getProxyURI,
getPutFileURI,
setSort,
getSort,
getDefaultLimit,
setDefaultLimit,
};