mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-04-15 02:39:05 +00:00
feat: add backend
This commit is contained in:
46
src/routes/BulkAddRoute.ts
Normal file
46
src/routes/BulkAddRoute.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCBulkAddRequest, SCBulkAddResponse, SCBulkAddRoute, SCNotFoundErrorResponse } from '@openstapps/core';
|
||||
import { logger } from '../common';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const bulkRouteModel = new SCBulkAddRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the bulk add route (SCBulkAddRoute)
|
||||
*/
|
||||
export const bulkAddRouter = createRoute<SCBulkAddResponse>(
|
||||
bulkRouteModel,
|
||||
async (request: SCBulkAddRequest, app, params) => {
|
||||
|
||||
if (!params || typeof params.UID !== 'string') {
|
||||
throw new Error('UID of Bulk was not given, but route with obligatory parameter was called');
|
||||
}
|
||||
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
const bulk = await bulkMemory.read(params.UID);
|
||||
|
||||
if (typeof bulk === 'undefined') {
|
||||
logger.warn(`Bulk with ${params.UID} not found.`);
|
||||
throw new SCNotFoundErrorResponse(app.get('isProductiveEnvironment'));
|
||||
}
|
||||
|
||||
await bulkMemory.database.post(request, bulk);
|
||||
|
||||
return {};
|
||||
},
|
||||
);
|
||||
46
src/routes/BulkDoneRoute.ts
Normal file
46
src/routes/BulkDoneRoute.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCBulkDoneRequest, SCBulkDoneResponse, SCBulkDoneRoute, SCNotFoundErrorResponse } from '@openstapps/core';
|
||||
import { logger } from '../common';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const bulkDoneRouteModel = new SCBulkDoneRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the bulk done request route (SCBulkDoneRoute)
|
||||
*/
|
||||
export const bulkDoneRouter = createRoute<SCBulkDoneResponse>(
|
||||
bulkDoneRouteModel,
|
||||
async (_request: SCBulkDoneRequest, app, params) => {
|
||||
|
||||
if (!params || typeof params.UID !== 'string') {
|
||||
throw new Error('UID of Bulk was not given, but route with obligatory parameter was called');
|
||||
}
|
||||
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
const bulk = await bulkMemory.read(params.UID);
|
||||
|
||||
if (typeof bulk === 'undefined') {
|
||||
logger.warn(`Bulk with ${params.UID} not found.`);
|
||||
throw new SCNotFoundErrorResponse(app.get('isProductiveEnvironment'));
|
||||
}
|
||||
|
||||
bulk.state = 'done';
|
||||
await bulkMemory.markAsDone(bulk);
|
||||
return {};
|
||||
},
|
||||
);
|
||||
31
src/routes/BulkRoute.ts
Normal file
31
src/routes/BulkRoute.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCBulkRequest, SCBulkResponse, SCBulkRoute } from '@openstapps/core';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const bulkRouteModel = new SCBulkRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the bulk request route (SCBulkRoute)
|
||||
*/
|
||||
export const bulkRouter = createRoute<SCBulkResponse>(
|
||||
bulkRouteModel,
|
||||
async (request: SCBulkRequest, app) => {
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
return await bulkMemory.create(request);
|
||||
},
|
||||
);
|
||||
43
src/routes/HTTPTypes.ts
Normal file
43
src/routes/HTTPTypes.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
export type HTTPVerb = 'all' |
|
||||
'get' |
|
||||
'post' |
|
||||
'put' |
|
||||
'delete' |
|
||||
'patch' |
|
||||
'options' |
|
||||
'head' |
|
||||
'checkout' |
|
||||
'copy' |
|
||||
'lock' |
|
||||
'merge' |
|
||||
'mkactivity' |
|
||||
'mkcol' |
|
||||
'move' |
|
||||
'm-search' |
|
||||
'notify' |
|
||||
'purge' |
|
||||
'report' |
|
||||
'search' |
|
||||
'subscribe' |
|
||||
'trace' |
|
||||
'unlock' |
|
||||
'unsubscribe';
|
||||
|
||||
export function isHttpMethod(method: string): method is HTTPVerb {
|
||||
return ['get', 'post', 'put'].indexOf(method) > -1;
|
||||
}
|
||||
32
src/routes/IndexRoute.ts
Normal file
32
src/routes/IndexRoute.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCConfigFile, SCIndexResponse, SCIndexRoute } from '@openstapps/core';
|
||||
import * as config from 'config';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const indexRouteModel = new SCIndexRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the index route (SCIndexRoute)
|
||||
*/
|
||||
export const indexRouter = createRoute<SCIndexResponse>(
|
||||
indexRouteModel,
|
||||
async (_request: SCIndexRoute, _app) => {
|
||||
const configObject: SCConfigFile = config.util.toObject();
|
||||
delete configObject.internal;
|
||||
return configObject;
|
||||
},
|
||||
);
|
||||
56
src/routes/MultiSearchRoute.ts
Normal file
56
src/routes/MultiSearchRoute.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
SCMultiSearchRequest,
|
||||
SCMultiSearchResponse,
|
||||
SCMultiSearchRoute,
|
||||
SCSearchResponse,
|
||||
SCTooManyRequestsErrorResponse,
|
||||
} from '@openstapps/core';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const multiSearchRouteModel = new SCMultiSearchRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the multi search route (SCMultiSearchRoute)
|
||||
*/
|
||||
export const multiSearchRouter = createRoute<SCMultiSearchResponse | SCTooManyRequestsErrorResponse>(
|
||||
multiSearchRouteModel,
|
||||
async (request: SCMultiSearchRequest, app) => {
|
||||
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
const queryNames = Object.keys(request);
|
||||
|
||||
if (queryNames.length > 5) {
|
||||
return new SCTooManyRequestsErrorResponse(app.get('isProductiveEnvironment'));
|
||||
}
|
||||
|
||||
// get a map of promises for each query
|
||||
const searchRequests = queryNames.map((queryName) => {
|
||||
return bulkMemory.database.search(request[queryName]);
|
||||
});
|
||||
|
||||
const listOfSearchResponses = await Promise.all(searchRequests);
|
||||
|
||||
const response: { [queryName: string]: SCSearchResponse } = {};
|
||||
queryNames.forEach((queryName, index) => {
|
||||
response[queryName] = listOfSearchResponses[index];
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
);
|
||||
159
src/routes/Route.ts
Normal file
159
src/routes/Route.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
SCInternalServerErrorResponse,
|
||||
SCMethodNotAllowedErrorResponse,
|
||||
SCRoute,
|
||||
SCValidationErrorResponse,
|
||||
} from '@openstapps/core';
|
||||
import { SCValidator } from '@openstapps/core-validator';
|
||||
import { Application, Router } from 'express';
|
||||
import PromiseRouter from 'express-promise-router';
|
||||
import { logger } from '../common';
|
||||
import { isHttpMethod } from './HTTPTypes';
|
||||
|
||||
/**
|
||||
* Creates a router from a route class (model of a route) and a handler function which implements the logic
|
||||
*
|
||||
* The given router performs a request and respone validation, sets status codes and checks if the given handler
|
||||
* only returns errors that are allowed for the client to see
|
||||
*
|
||||
* @param routeClass
|
||||
* @param handler
|
||||
*/
|
||||
export function createRoute<RETURNTYPE>(
|
||||
routeClass: SCRoute,
|
||||
handler: (validatedBody: any, app: Application, params?: { [parameterName: string]: string }) => Promise<RETURNTYPE>,
|
||||
): Router {
|
||||
// create router
|
||||
const router = PromiseRouter({ mergeParams: true });
|
||||
|
||||
// create route
|
||||
// the given type has no index signature so we have to cast to get the IRouteHandler when a HTTP method is given
|
||||
const route = router.route(routeClass.urlFragment);
|
||||
|
||||
// get route parameters (path parameters)
|
||||
if (Array.isArray(routeClass.obligatoryParameters) && routeClass.obligatoryParameters.length > 0) {
|
||||
routeClass.obligatoryParameters.forEach((parameterName) => {
|
||||
router.param(parameterName, async (_req, _res, next, _parameterValue: string) => {
|
||||
|
||||
// if (typeof req.params === 'undefined') {
|
||||
// req.params = {};
|
||||
// }
|
||||
|
||||
// set parameter values on request object
|
||||
// req.params[parameterName] = parameterValue;
|
||||
// hand over the request to the next handler (our method route handler)
|
||||
next();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const verb = routeClass.method.toLowerCase();
|
||||
|
||||
// check if route has a valid http verb
|
||||
if (isHttpMethod(verb)) {
|
||||
// create a route handler for the given HTTP method
|
||||
route[verb](async (req, res) => {
|
||||
|
||||
try {
|
||||
// get the core validator from the app
|
||||
const validator: SCValidator = req.app.get('validator');
|
||||
|
||||
// validate request
|
||||
const requestValidation = validator.validate(req.body, routeClass.requestBodyName.substring(2));
|
||||
|
||||
if (requestValidation.errors.length > 0) {
|
||||
const error = new SCValidationErrorResponse(
|
||||
requestValidation.errors,
|
||||
req.app.get('isProductiveEnvironment'),
|
||||
);
|
||||
res.status(error.statusCode);
|
||||
res.json(error);
|
||||
logger.warn(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const params: { [parameterName: string]: string } = {};
|
||||
|
||||
if (Array.isArray(routeClass.obligatoryParameters) && routeClass.obligatoryParameters) {
|
||||
// copy over parameter values from request object
|
||||
// the parameter values were set in the parameter handler of the route
|
||||
routeClass.obligatoryParameters.forEach((parameterName) => {
|
||||
params[parameterName] = req.params[parameterName];
|
||||
});
|
||||
}
|
||||
|
||||
// hand over request to handler with path parameters
|
||||
const response = await handler(req.body, req.app, params);
|
||||
|
||||
// validate response generated by handler
|
||||
const responseValidation = validator.validate(response, routeClass.responseBodyName.substring(2));
|
||||
|
||||
if (responseValidation.errors.length > 0) {
|
||||
const validationError = new SCValidationErrorResponse(
|
||||
responseValidation.errors,
|
||||
req.app.get('isProductiveEnvironment'),
|
||||
);
|
||||
const internalServerError = new SCInternalServerErrorResponse(
|
||||
validationError,
|
||||
req.app.get('isProductiveEnvironment'),
|
||||
);
|
||||
res.status(internalServerError.statusCode);
|
||||
res.json(internalServerError);
|
||||
logger.warn(internalServerError);
|
||||
return;
|
||||
}
|
||||
|
||||
// set status code
|
||||
res.status(routeClass.statusCodeSuccess);
|
||||
|
||||
// respond
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
// if the error response is allowed on the route
|
||||
if (routeClass.errorNames.indexOf(error.constructor.name) > -1) {
|
||||
// respond with the error from the handler
|
||||
res.status(error.statusCode);
|
||||
res.json(error);
|
||||
logger.warn(error);
|
||||
} else {
|
||||
// the error is not allowed so something went wrong
|
||||
const internalServerError = new SCInternalServerErrorResponse(
|
||||
error,
|
||||
req.app.get('isProductiveEnvironment'),
|
||||
);
|
||||
res.status(internalServerError.statusCode);
|
||||
res.json(internalServerError);
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid HTTP verb in route definition. Please check route definitions in `@openstapps/core`');
|
||||
}
|
||||
|
||||
// return a SCMethodNotAllowedErrorResponse on all other HTTP methods
|
||||
route.all((req, res) => {
|
||||
const error = new SCMethodNotAllowedErrorResponse(req.app.get('isProductiveEnvironment'));
|
||||
res.status(error.statusCode);
|
||||
res.json(error);
|
||||
logger.warn(error);
|
||||
});
|
||||
|
||||
// return router
|
||||
return router;
|
||||
}
|
||||
28
src/routes/SearchRoute.ts
Normal file
28
src/routes/SearchRoute.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCSearchRequest, SCSearchResponse, SCSearchRoute } from '@openstapps/core';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const searchRouteModel = new SCSearchRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the search route (SCSearchRoute)
|
||||
*/
|
||||
export const searchRouter = createRoute<SCSearchResponse>(searchRouteModel, async ( request: SCSearchRequest, app) => {
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
return await bulkMemory.database.search(request);
|
||||
});
|
||||
32
src/routes/ThingUpdateRoute.ts
Normal file
32
src/routes/ThingUpdateRoute.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { SCThingUpdateRequest, SCThingUpdateResponse, SCThingUpdateRoute } from '@openstapps/core';
|
||||
import { BulkStorage } from '../storage/BulkStorage';
|
||||
import { createRoute } from './Route';
|
||||
|
||||
const thingUpdateRouteModel = new SCThingUpdateRoute();
|
||||
|
||||
/**
|
||||
* Implementation of the thing update route (SCThingUpdateRoute)
|
||||
*/
|
||||
export const thingUpdateRouter = createRoute<SCThingUpdateResponse>(
|
||||
thingUpdateRouteModel,
|
||||
async (request: SCThingUpdateRequest, app) => {
|
||||
const bulkMemory: BulkStorage = app.get('bulk');
|
||||
await bulkMemory.database.put(request);
|
||||
return {};
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user