diff --git a/API.paw b/API.paw index 18697cc4589c23693eb11a1af634fb2b34a16d3a..a6f9d7b8a2c00fc507fc48ad9a1fc741754e977a 100644 Binary files a/API.paw and b/API.paw differ diff --git a/src/controller/ComponentController.ts b/src/controller/ComponentController.ts index 587993f56c2560bc03c361057f311f95db6d6bd5..8fca5ec7a7bac88582cf4c77fc9c6ca4490aef98 100644 --- a/src/controller/ComponentController.ts +++ b/src/controller/ComponentController.ts @@ -7,6 +7,7 @@ import { NodeObject } from "jsonld"; import BaseController from "./BaseController"; import TypeDefinition from "../entity/TypeDefinition"; import ComponentInformation from "../entity/ComponentInformation"; +import APIError from "./util/APIError"; const logger = logService(module); @@ -33,7 +34,8 @@ export default class ComponentController extends BaseController { // It won't have valid context information anyway and is only intended // for internal use. response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Component not found.")); return; } @@ -44,12 +46,13 @@ export default class ComponentController extends BaseController { response.send(componentJsonLd); } else { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Component not found.")); } }).catch((error) => { logger.error(`Error while retrieving component: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + response.send(new APIError(JSON.stringify(error))); }); } @@ -74,7 +77,8 @@ export default class ComponentController extends BaseController { Component.find(filter, fromDate, toDate, pageSize, page, this.componentRepository).then((components) => { if(components.length == 0) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No component found for this query.")); return; } @@ -83,7 +87,8 @@ export default class ComponentController extends BaseController { }).catch((error) => { logger.error(`Error while searching for components: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(JSON.stringify(error))); }); } @@ -95,7 +100,8 @@ export default class ComponentController extends BaseController { response.send(result); } else { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No roots found.")); } } @@ -115,7 +121,8 @@ export default class ComponentController extends BaseController { if(!parentComponent) { logger.error("Cannot find component with id: ", request.params.parentComponentId); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Component with id ${parentComponentId} does not exist.`)); return; } @@ -123,7 +130,8 @@ export default class ComponentController extends BaseController { if(!type) { response.status(404); - response.send({ message: "Type definition not found." }); + this.setJSONLDResponseType(response); + response.send(new APIError("Type definition not found.")); return; } @@ -132,7 +140,8 @@ export default class ComponentController extends BaseController { if(currentinformationWithTopic.length > 0) { // The topic is already in use by other information. response.status(412); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Topic currently in use by another ComponentInformation.")); return; } @@ -152,7 +161,8 @@ export default class ComponentController extends BaseController { } catch(error) { logger.error(`Error while searching for measurement targets: ${JSON.stringify(error)}`); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while searching for target: ${JSON.stringify(error)}`)); return; } } @@ -165,7 +175,8 @@ export default class ComponentController extends BaseController { // Verification of information schema failed. logger.error(`information verification failed: ${error}`); response.status(400); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Component Information verification failed: ${error}`)); return; } @@ -177,12 +188,7 @@ export default class ComponentController extends BaseController { } else { response.status(400); this.setJSONLDResponseType(response); - response.send({ - "@context": { - "message": "https://schema.org/error" - }, - "message": "License URL for parent relation not set." - }); + response.send(new APIError("License URL for parent relation not set.")); return; } } else { @@ -200,7 +206,8 @@ export default class ComponentController extends BaseController { }).catch((error) => { logger.error(`Error while retrieving parent component: ${error}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while retrieving parent component: ${JSON.stringify(error)}`)); }); } diff --git a/src/controller/ComponentInformationController.ts b/src/controller/ComponentInformationController.ts index a2274e36155671c6a89a80bb406d58fc26b39516..dd2cbccc331ea585598e75cb1c71c278033854df 100644 --- a/src/controller/ComponentInformationController.ts +++ b/src/controller/ComponentInformationController.ts @@ -5,6 +5,7 @@ import logService from "../services/logger"; import BaseController from "./BaseController"; import TypeDefinition from "../entity/TypeDefinition"; import Component from "../entity/Component"; +import APIError from "./util/APIError"; const logger = logService(module); @@ -34,7 +35,8 @@ export default class ComponentInformationController extends BaseController { }).catch((error) => { logger.error(`Cannot find information by Id: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Cannot find information by Id: ${JSON.stringify(error)}`)); }); } @@ -60,7 +62,8 @@ export default class ComponentInformationController extends BaseController { ComponentInformation.find(filter, fromDate, toDate, metadataTypeFilter, pageSize, page, this.componentInformationRepository).then(async (informationArray) => { if(informationArray.length == 0) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No result found for this query.")); return; } @@ -69,7 +72,8 @@ export default class ComponentInformationController extends BaseController { }).catch((error) => { logger.error(`Error while searching component information: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while searching component information: ${JSON.stringify(error)}`)); }); } @@ -78,9 +82,10 @@ export default class ComponentInformationController extends BaseController { this.componentInformationRepository.findOne(oldVersionId, { relations: ["component", "nextVersion"] }).then(async (oldVersionInformation) => { if (!oldVersionInformation) { - logger.error("Cannot find information with id: ", request.params.oldVersionId); + logger.error(`Cannot find information with id: ${request.params.oldVersionId}`); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Cannot find information with id: ${request.params.oldVersionId}`)); return; } @@ -89,7 +94,8 @@ export default class ComponentInformationController extends BaseController { if (oldVersionInformation.nextVersion != null) { // 412: Precondition failed response.status(412); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Information already has a new version set.")); return; } @@ -110,7 +116,8 @@ export default class ComponentInformationController extends BaseController { // Topic is already related by a information object which is not // the old version. response.status(412); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Topic is already in use by another component information.")); return; } } @@ -119,14 +126,16 @@ export default class ComponentInformationController extends BaseController { if(!type) { response.status(404); - response.send({ message: "Type definition not found." }); + this.setJSONLDResponseType(response); + response.send(new APIError("Type definition not found.")); return; } if (!type.validateData(metadata)) { // Verification of information schema failed. response.status(400); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Verification of information schema failed.")); return; } @@ -146,7 +155,8 @@ export default class ComponentInformationController extends BaseController { } catch(error) { logger.error(`Error while searching for measurement targets: ${JSON.stringify(error)}`); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while searching for measurement targets: ${JSON.stringify(error)}`)); return; } } @@ -160,7 +170,8 @@ export default class ComponentInformationController extends BaseController { }).catch((error) => { logger.error(`Error while retrieving old version: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while retrieving old version: ${JSON.stringify(error)}`)); }); } diff --git a/src/controller/ComponentRelationController.ts b/src/controller/ComponentRelationController.ts index 5362763da1b3bbe7d3737e11113dab95e9b54a87..9c075b6129522a6ff88070310a5dc2e9f4f79a94 100644 --- a/src/controller/ComponentRelationController.ts +++ b/src/controller/ComponentRelationController.ts @@ -4,6 +4,7 @@ import ComponentRelation from "../entity/ComponentRelation"; import Component from "../entity/Component"; import BaseController from "./BaseController"; import logService from "../services/logger"; +import APIError from "./util/APIError"; const logger = logService(module); @@ -27,12 +28,14 @@ export default class ComponentRelationController extends BaseController { response.send(relation.toJSONLD()); } else { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Relation not found.")); } }).catch((error) => { logger.error(`Cannot find relation by Id: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Cannot find relation by Id: ${JSON.stringify(error)}`)); }); } @@ -44,7 +47,8 @@ export default class ComponentRelationController extends BaseController { ComponentRelation.find(filter, pageSize, page, this.componentRelationRepository).then((relations) => { if(relations.length == 0) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No relation found.")); return; } @@ -53,7 +57,8 @@ export default class ComponentRelationController extends BaseController { }).catch((error) => { logger.error(`Error while searching component relation: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while searching component relation: ${JSON.stringify(error)}`)); }); } @@ -64,7 +69,8 @@ export default class ComponentRelationController extends BaseController { if(!component) { logger.error(`Cannot find component with id: ${JSON.stringify(request.body.componentId)}`); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Cannot find component with id: ${JSON.stringify(request.body.componentId)}`)); return; } @@ -73,35 +79,41 @@ export default class ComponentRelationController extends BaseController { if(!newParentComponent) { logger.error(`Cannot find parent component with id: ${request.body.newParentComponentId}`); response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Cannot find parent component with id: ${request.body.newParentComponentId}`)); return; } if(component == null) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Component not found.")); return; } // Ensure that the user does not try to move the root itself. if(Component.rootId == component.id) { response.status(400); - response.send(); + this.setJSONLDResponseType(response); + // Pretend that this internal root does not exist. + response.send(new APIError("Component not found.")); return; } if(newParentComponent.id == component.id) { // A component cannot be a parent of itself. response.status(400); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("A component cannot be a parent of itself.")); return; } const oldRelation: ComponentRelation | undefined = await ComponentRelation.findParentRelation(component, this.componentRelationRepository); if(component == null) { + this.setJSONLDResponseType(response); response.status(404); - response.send(); + response.send(new APIError("Component not found.")); return; } @@ -137,8 +149,9 @@ export default class ComponentRelationController extends BaseController { } catch (error) { logger.error(`Error while creating a relation: ${JSON.stringify(error)}`); await queryRunner.rollbackTransaction(); + this.setJSONLDResponseType(response); response.status(500); - response.send(error); + response.send(new APIError(`Error while creating a relation: ${JSON.stringify(error)}`)); } finally { await queryRunner.release(); } diff --git a/src/controller/MeasurementController.ts b/src/controller/MeasurementController.ts index 751a9f613ddf8de8ff168edacfbf61dd15d63ada..0bcf1f850bd31537f715b9e8439101a885ea91c6 100644 --- a/src/controller/MeasurementController.ts +++ b/src/controller/MeasurementController.ts @@ -3,6 +3,7 @@ import { Repository, Connection } from "typeorm"; import Measurement from "../entity/Measurement"; import logService from "../services/logger"; import BaseController from "./BaseController"; +import APIError from "./util/APIError"; const logger = logService(module); @@ -24,13 +25,15 @@ export default class MeasurementController extends BaseController { this.setJSONLDResponseType(response); response.send(measurementJsonLd); } else { + this.setJSONLDResponseType(response); response.status(404); - response.send(); + response.send(new APIError("Measurement not found.")); } }).catch((error) => { logger.error(`Error while retrieving measurement: ${JSON.stringify(error)}`); response.status(500); - response.send(error); + this.setJSONLDResponseType(response); + response.send(`Error while retrieving measurement: ${JSON.stringify(error)}`); }); } @@ -58,17 +61,18 @@ export default class MeasurementController extends BaseController { Measurement.find(filter, fromDate, toDate, valueTypeFilter, metadataTypeFilter, informationTypeFilter, pageSize, page, this.measurementRepository).then((results) => { if(results.length == 0) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No measurement found.")); return; } this.setJSONLDResponseType(response); response.send(results.map(measurement => measurement.toJSONLD())); }).catch((error) => { - // TODO logger.error(`Error while querying measurements: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while querying measurements: ${JSON.stringify(error)}`)); }); }; diff --git a/src/controller/TypeDefinitionController.ts b/src/controller/TypeDefinitionController.ts index dede4f2512de8956df442c2c17d896becfb2a96e..730c66b68879cda4370852429fe2e95f66c69b44 100644 --- a/src/controller/TypeDefinitionController.ts +++ b/src/controller/TypeDefinitionController.ts @@ -3,6 +3,7 @@ import TypeDefinition from "../entity/TypeDefinition"; import logService from "../services/logger"; import BaseController from "./BaseController"; import { Connection, Repository } from "typeorm"; +import APIError from "./util/APIError"; const logger = logService(module); @@ -21,12 +22,14 @@ export default class TypeDefinitionController extends BaseController { response.send(type); } else { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("Type Definition not found.")); } }).catch((error) => { logger.error(`Error while searching for type ${request.body.name}: ${error}`); response.status(500); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while searching for type ${request.body.name}: ${error}`)); }); } @@ -39,7 +42,8 @@ export default class TypeDefinitionController extends BaseController { TypeDefinition.search(name ?? "", pageSize, page, this.repository).then((results) => { if(results.length == 0) { response.status(404); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("No Type Definition found.")); return; } @@ -47,7 +51,8 @@ export default class TypeDefinitionController extends BaseController { }).catch((error) => { logger.error(`Error while searching type definitions: ${JSON.stringify(error)}`); response.status(400); - response.send(error); + this.setJSONLDResponseType(response); + response.send(`Error while searching type definitions: ${JSON.stringify(error)}`); }); } @@ -59,9 +64,8 @@ export default class TypeDefinitionController extends BaseController { TypeDefinition.validateSchema(schema); } catch(error) { response.status(400); - response.send({ - message: (error as Error).toString() - }); + this.setJSONLDResponseType(response); + response.send(new APIError((error as Error).toString())); return; } @@ -83,12 +87,14 @@ export default class TypeDefinitionController extends BaseController { if(error.code == "23505") { // 412 - Precondition Failed response.status(412); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError("A type definition with this name already exists.")); return; } response.status(500); - response.send(); + this.setJSONLDResponseType(response); + response.send(new APIError(`Error while saving new type definition: ${JSON.stringify(error)}`)); }); } diff --git a/src/controller/util/APIError.ts b/src/controller/util/APIError.ts new file mode 100644 index 0000000000000000000000000000000000000000..7cb7dfadb5f0ac3ed22fae9f8164b620f8e970c8 --- /dev/null +++ b/src/controller/util/APIError.ts @@ -0,0 +1,19 @@ + +export default class APIError { + + message: string; + + constructor(message: string) { + this.message = message; + } + + toJSON(): unknown { + return { + "@context": { + "message": "https://schema.org/error" + }, + "message": this.message + }; + } + +} \ No newline at end of file diff --git a/src/middleware/ValidationErrorMiddleware.ts b/src/middleware/ValidationErrorMiddleware.ts index f43008615153f229ddfbe8ef0c1cb3f52194aa51..425c37d5f9e743c7b2b5e6ca7e704d67d17f6071 100644 --- a/src/middleware/ValidationErrorMiddleware.ts +++ b/src/middleware/ValidationErrorMiddleware.ts @@ -1,5 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { validationResult } from "express-validator"; +import APIError from "../controller/util/APIError"; export default (request: Request, response: Response, next: NextFunction): void => { const result = validationResult(request); @@ -12,12 +13,7 @@ export default (request: Request, response: Response, next: NextFunction): void response.type("application/ld+json"); response.send( result.array().map((error) => { - return { - "@context": { - "message": "https://schema.org/error" - }, - "message": error.msg - }; + return new APIError(error.msg); }) ); return;