diff --git a/models/task.js b/models/task.js index edf8018..b142ff1 100644 --- a/models/task.js +++ b/models/task.js @@ -7,6 +7,7 @@ import { update, uuid, } from 'mu'; +import { prefixMap } from '../support/prefixes'; export const TASK_TYPE_SIGNING_DECISION_LIST = 'decisionListSignature'; export const TASK_TYPE_PUBLISHING_DECISION_LIST = 'decisionListPublication'; @@ -20,6 +21,20 @@ export const TASK_STATUS_SUCCESS = 'http://lblod.data.gift/besluit-publicatie-melding-statuses/success'; export const TASK_STATUS_RUNNING = 'http://lblod.data.gift/besluit-publicatie-melding-statuses/ongoing'; +export class TaskError { + constructor({ id, uri, message }) { + if (uri) { + // we don't want to generate a new id if we got a uri, even if it's null + this.id = id; + this.uri = uri; + } else { + this.id = id ?? uuid(); + this.uri = `http://redpencil.data.gift/id/jobs/error/${this.id}`; + } + + this.message = message; + } +} export default class Task { static async create(meeting, type) { @@ -58,12 +73,13 @@ export default class Task { static async find(uuid) { const result = await query(` - PREFIX mu: - PREFIX nuao: - PREFIX task: - PREFIX dct: - PREFIX adms: - SELECT ?uri ?uuid ?type ?involves ?status ?modified ?created WHERE { + ${prefixMap.get('mu').toSparqlString()} + ${prefixMap.get('nuao').toSparqlString()} + ${prefixMap.get('task').toSparqlString()} + ${prefixMap.get('dct').toSparqlString()} + ${prefixMap.get('adms').toSparqlString()} + ${prefixMap.get('oslc').toSparqlString()} + SELECT ?uri ?uuid ?type ?involves ?status ?modified ?created ?error ?errorId ?errorMessage WHERE { BIND(${sparqlEscapeString(uuid)} AS ?uuid) ?uri a task:Task; mu:uuid ?uuid; @@ -73,6 +89,11 @@ export default class Task { nuao:involves ?involves; dct:creator ; adms:status ?status. + OPTIONAL { + ?uri task:error ?error. + ?error mu:uuid ?errorId. + ?error oslc:message ?errorMessage. + } } `); if (result.results.bindings.length) { @@ -82,12 +103,13 @@ export default class Task { static async query({ meetingUri, type, userUri = null }) { const result = await query(` - PREFIX mu: - PREFIX nuao: - PREFIX task: - PREFIX dct: - PREFIX adms: - SELECT ?uri ?uuid ?type ?involves ?status ?modified ?created WHERE { + ${prefixMap.get('mu').toSparqlString()} + ${prefixMap.get('nuao').toSparqlString()} + ${prefixMap.get('task').toSparqlString()} + ${prefixMap.get('dct').toSparqlString()} + ${prefixMap.get('adms').toSparqlString()} + ${prefixMap.get('oslc').toSparqlString()} + SELECT ?uri ?uuid ?type ?involves ?status ?modified ?created ?error ?errorId ?errorMessage WHERE { ?uri a task:Task; mu:uuid ?uuid; dct:type ${sparqlEscapeString(type)}; @@ -96,6 +118,12 @@ export default class Task { nuao:involves ${sparqlEscapeUri(meetingUri)}; dct:creator ; adms:status ?status. + + OPTIONAL { + ?uri task:error ?error. + ?error mu:uuid ?errorId. + ?error oslc:message ?errorMessage. + } ${userUri ? `?uri nuao:involves ${sparqlEscapeUri(userUri)}.` : ''} } `); @@ -109,6 +137,15 @@ export default class Task { } static fromBinding(binding) { + let taskError = null; + if (binding.error?.value) { + taskError = new TaskError({ + uri: binding.error.value, + id: binding.errorId?.value, + message: binding.errorMessage?.value, + }); + } + return new Task({ id: binding.uuid.value, uri: binding.uri.value, @@ -117,10 +154,11 @@ export default class Task { status: binding.status.value, involves: binding.involves.value, type: binding.type.value, + error: taskError, }); } - constructor({ id, uri, created, status, modified, type, involves }) { + constructor({ id, uri, created, status, modified, type, involves, error }) { this.id = id; this.type = type; this.involves = involves; @@ -128,29 +166,49 @@ export default class Task { this.modified = modified; this.status = status; this.uri = uri; + this.error = error; } async updateStatus(status, reason) { + let taskError = null; + if (reason) { + taskError = new TaskError({ message: reason }); + } + //prettier-ignore const queryString = ` - PREFIX mu: - PREFIX nuao: - PREFIX task: - PREFIX dct: - PREFIX adms: - PREFIX rdfs: + ${prefixMap.get("mu").toSparqlString()} + ${prefixMap.get("task").toSparqlString()} + ${prefixMap.get("adms").toSparqlString()} + ${prefixMap.get("oslc").toSparqlString()} DELETE { ?uri adms:status ?status. + ?uri task:error ?error. + ?error ?errorP ?errorV. } INSERT { ?uri adms:status ${sparqlEscapeUri(status)}. - ${reason ? `?uri rdfs:comment ${sparqlEscapeString(reason)}.` : ''} + ${ + taskError + ? `?uri task:error ${sparqlEscapeUri(taskError.uri)}. + ${sparqlEscapeUri(taskError.uri)} a oslc:Error. + ${sparqlEscapeUri(taskError.uri)} mu:uuid ${sparqlEscapeString(taskError.id)}. + ${sparqlEscapeUri(taskError.uri)} oslc:message ${sparqlEscapeString(taskError.message)}.` + : '' + } } WHERE { - ?uri a task:Task; - mu:uuid ${sparqlEscapeString(this.id)}; - adms:status ?status. + ?uri a task:Task; + mu:uuid ${sparqlEscapeString(this.id)}; + adms:status ?status. + OPTIONAL { + ?uri task:error ?error. + ?error ?errorP ?errorV. + } }`; await update(queryString); + this.status = status; + + this.error = taskError; } } diff --git a/models/versioned-notulen.js b/models/versioned-notulen.js index 155a732..b447f09 100644 --- a/models/versioned-notulen.js +++ b/models/versioned-notulen.js @@ -30,7 +30,8 @@ export default class VersionedNotulen { const bindings = r.results.bindings; if (bindings.length > 0) { const binding = bindings[0]; - const fileUri = bindings.fileUri?.value; + + const fileUri = binding.fileUri?.value; let html; if (fileUri) { html = await getFileContentForUri(fileUri); diff --git a/routes/task.js b/routes/task.js index 9cfdc66..d1cd392 100644 --- a/routes/task.js +++ b/routes/task.js @@ -10,9 +10,20 @@ router.get('/publication-tasks/:id', async function (req, res) { res.status(200).send({ data: { id: task.id, + uri: task.uri, status: task.status, type: task.type, + created: task.created, + modified: task.modified, + involves: task.involves, taskType: task.type, + error: task.error + ? { + id: task.error.id, + message: task.error.message, + uri: task.error.uri, + } + : undefined, }, }); } else { diff --git a/support/pre-importer.js b/support/pre-importer.js index 0672663..ae484a2 100644 --- a/support/pre-importer.js +++ b/support/pre-importer.js @@ -54,16 +54,24 @@ async function getVersionedContent(uri, contentPredicate) { )} prov:generated/^nie:dataSource ?physicalFileUri. } }`; const result = await query(contentQuery); - if (result.results.bindings.length == 1) { - const binding = result.results.bindings[0]; - if (binding.content) { - return binding.content.value; - } else if (binding.physicalFileUri) { + const bindings = result.results.bindings; + if (bindings.length === 1) { + const binding = bindings[0]; + if (binding.physicalFileUri) { const content = await getFileContentForUri(binding.physicalFileUri.value); return content; + } else if (binding.content) { + return binding.content.value; } + } else if (bindings.length > 1) { + throw new Error( + `Found ${bindings.length} sources of content for versioned resource ${uri}. Only one is allowed. This means there is invalid data in the database.` + ); + } else { + throw new Error( + `Found no sources of content for versioned resource ${uri}` + ); } - throw new Error('could not retrieve content'); } async function handleVersionedResource( diff --git a/support/prefixes.js b/support/prefixes.js index ec6617c..7a0fe02 100644 --- a/support/prefixes.js +++ b/support/prefixes.js @@ -68,8 +68,11 @@ const prefixMap = new Map([ ["notulen", new Prefix("notulen", "http://lblod.data.gift/vocabularies/notulen/")], ["nfo", new Prefix("nfo", "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#")], ["nie", new Prefix("nie", "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#")], + ["gn", new Prefix("gn", "http://data.lblod.info/vocabularies/gelinktnotuleren/")], ["dbpedia", new Prefix("dbpedia", "http://dbpedia.org/ontology/")], - ["gn", new Prefix("gn", "http://data.lblod.info/vocabularies/gelinktnotuleren/")] + ["oslc", new Prefix("oslc", "http://open-services.net/ns/core#")], + ["task", new Prefix("task", "http://redpencil.data.gift/vocabularies/tasks/")], + ["nuao", new Prefix("nuao", "http://www.semanticdesktop.org/ontologies/2010/01/25/nuao#")], ]); export { prefixes, prefixMap };