Added Swagger docs
MODIFY
package.json
package.json
"source-map-support": "^0.5.19",
"stripe": "^7.15.0",
"strong-soap": "^1.22.1",
+ "swagger-ui-express": "^4.1.4",
"tslib": "^2.0.3",
"tz-lookup": "^6.1.25",
"urlencode": "^1.1.0",

ADD
src/assets/server/rest/docs/open-api-standard.json
+{
+ "openapi": "3.0.3",
+ "servers": [
+ {
+ "description": "SCP - Prod - SAP Labs France",
+ "url": "https://slf.evse.cfapps.eu10.hana.ondemand.com/client/api/1.0.0/"
+ },
+ {
+ "description": "SCP - Prod - Tenant Management",
+ "url": "https://evse.cfapps.eu10.hana.ondemand.com/client/api/1.0.0/"
+ },
+ {
+ "description": "AWS - Prod - Tenant Management",
+ "url": "https://e-mobility-group.com/client/api/1.0.0/"
+ }
+ ],
+ "info": {
+ "title": "e-Mobility REST API",
+ "description": "Charging Station Management Software",
+ "version": "1.0.0",
+ "contact": {
+ "name": "Serge FABIANO",
+ "email": "serge.fabiano@sap.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "https://github.com/LucasBrazi06/ev-server/blob/master/LICENSE"
+ }
+ },
+ "tags": [
+ {
+ "name": "Tenants",
+ "description": "Tenant management"
+ }
+ ],
+ "paths": {
+ "/tenants": {
+ "post": {
+ "description": "Create a Tenant",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Tenant"
+ }
+ }
+ }
+ },
+ "tags": [
+ "Super Admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "Tenant is created with success"
+ },
+ "401": {
+ "$ref": "#/components/responses/UnauthorizedError"
+ },
+ "500": {
+ "$ref": "#/components/responses/500BackendError"
+ }
+ }
+ },
+ "get": {
+ "description": "Retrieve a list of Tenants",
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "Search",
+ "description": "Search on common Tenant's fields",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "WithLogo",
+ "description": "Request Tenants with their logos",
+ "example": false,
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "WithComponents",
+ "description": "Request Tenants with their components",
+ "example": false,
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "WithAddress",
+ "description": "Request Tenants with their address",
+ "example": false,
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "$ref": "#/components/parameters/Limit"
+ },
+ {
+ "$ref": "#/components/parameters/Skip"
+ },
+ {
+ "$ref": "#/components/parameters/SortFields"
+ },
+ {
+ "$ref": "#/components/parameters/SortDirs"
+ }
+ ],
+ "tags": [
+ "Tenants"
+ ],
+ "responses": {
+ "200": {
+ "description": "Return the tenant list",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Tenant"
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "$ref": "#/components/responses/UnauthorizedError"
+ },
+ "500": {
+ "$ref": "#/components/responses/500BackendError"
+ }
+ }
+ }
+ },
+ "/tenants/{tenantID}": {
+ "get": {
+ "description": "Retrieve a Tenant",
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "tenantID",
+ "description": "Tenant ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "tags": [
+ "Tenants"
+ ],
+ "responses": {
+ "200": {
+ "description": "Return the found Tenant",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Tenant"
+ }
+ }
+ }
+ },
+ "401": {
+ "$ref": "#/components/responses/UnauthorizedError"
+ },
+ "500": {
+ "$ref": "#/components/responses/500BackendError"
+ }
+ }
+ },
+ "delete": {
+ "description": "Delete a Tenant",
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "tenantID",
+ "description": "Tenant ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "tags": [
+ "Tenants"
+ ],
+ "responses": {
+ "200": {
+ "description": "Tenant has been successfully deleted"
+ },
+ "401": {
+ "$ref": "#/components/responses/UnauthorizedError"
+ },
+ "500": {
+ "$ref": "#/components/responses/500BackendError"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "bearerAuth": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT"
+ }
+ },
+ "responses": {
+ "UnauthorizedError": {
+ "description": "Access token is missing or invalid"
+ },
+ "500BackendError": {
+ "description": "Unexpected Backend error, checks the Logs"
+ }
+ },
+ "schemas": {
+ "Tenant": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "email",
+ "subdomain"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": "5be7fb271014d90008992347"
+ },
+ "name": {
+ "type": "string",
+ "example": "SAP Labs France"
+ },
+ "email": {
+ "type": "string",
+ "example": "serge.fabiano@sap.com"
+ },
+ "subdomain": {
+ "type": "string",
+ "example": "slf"
+ },
+ "logo": {
+ "type": "string",
+ "example": "data:image/png;base64,iVBORw0KGgoAAAANS"
+ },
+ "address": {
+ "$ref": "#/components/schemas/Address"
+ },
+ "createdBy": {
+ "$ref": "#/components/schemas/CreatedBy"
+ },
+ "createdOn": {
+ "$ref": "#/components/schemas/CreatedOn"
+ },
+ "lastChangedBy": {
+ "$ref": "#/components/schemas/LastChangedBy"
+ },
+ "lastChangedOn": {
+ "$ref": "#/components/schemas/LastChangedOn"
+ },
+ "components": {
+ "type": "object",
+ "properties": {
+ "ocpi": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "gireve"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "refund": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "concur"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "pricing": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "simple",
+ "convergentCharging"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "billing": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "stripe"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "organization": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "statistics": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "analytics": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "sac"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "smartCharging": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "sapSmartCharging"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "asset": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "car": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "Address": {
+ "type": "object",
+ "properties": {
+ "address1": {
+ "type": "string",
+ "example": "805 av. du Docteur Maurice Donat"
+ },
+ "address2": {
+ "type": "string"
+ },
+ "postalCode": {
+ "type": "string",
+ "example": "06250"
+ },
+ "city": {
+ "type": "string",
+ "example": "Mougins"
+ },
+ "department": {
+ "type": "string",
+ "example": "Alpes Maritimes"
+ },
+ "region": {
+ "type": "string",
+ "example": "PACA"
+ },
+ "country": {
+ "type": "string",
+ "example": "France"
+ },
+ "coordinates": {
+ "type": "array",
+ "example": [
+ 43.6121053,
+ 7.016958099999999
+ ],
+ "items": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "LastChangedBy": {
+ "type": "string"
+ },
+ "LastChangedOn": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2020-07-22T17:02:42.036Z"
+ },
+ "CreatedBy": {
+ "type": "string"
+ },
+ "CreatedOn": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2020-07-22T17:02:42.036Z"
+ }
+ },
+ "parameters": {
+ "Limit": {
+ "in": "query",
+ "name": "Limit",
+ "description": "Limit the number of records",
+ "example": 100,
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "Skip": {
+ "in": "query",
+ "name": "Skip",
+ "description": "Skip number of records",
+ "example": 0,
+ "required": false,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "SortFields": {
+ "in": "query",
+ "name": "SortFields",
+ "description": "Sorted fields",
+ "example": "id",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "SortDirs": {
+ "in": "query",
+ "name": "SortDirs",
+ "description": "Sorted directions",
+ "required": false,
+ "example": "asc",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file

MODIFY
src/server/ocpp/validation/OCPPValidation.ts
src/server/ocpp/validation/OCPPValidation.ts
@@ -3,7 +3,7 @@ import { OCPPAuthorizeRequestExtended, OCPPBootNotificationRequestExtended, OCPP
import BackendError from '../../../exception/BackendError';
import ChargingStation from '../../../types/ChargingStation';
import Logging from '../../../utils/Logging';
-import SchemaValidator from '../../rest/validation/SchemaValidator';
+import SchemaValidator from '../../rest/validator/SchemaValidator';
import { ServerAction } from '../../../types/Server';
import Utils from '../../../utils/Utils';
import fs from 'fs';

MODIFY
src/server/rest/CentralRestServer.ts
src/server/rest/CentralRestServer.ts
@@ -20,6 +20,7 @@ import http from 'http';
import sanitize from 'express-sanitizer';
import socketio from 'socket.io';
import socketioJwt from 'socketio-jwt';
+import { swaggerRouter } from './router/SwaggerRouter';
import { tenantRouter } from './router/TenantRouter';
const MODULE_NAME = 'CentralRestServer';
@@ -50,6 +51,7 @@ export default class CentralRestServer {
this.expressApplication.use(CentralRestServerAuthentication.initialize());
// Routers
this.expressApplication.use('/v1/api', CentralRestServerAuthentication.authenticate(), tenantRouter);
+ this.expressApplication.use('/v1/docs', swaggerRouter);
// Auth services
this.expressApplication.all('/client/auth/:action', CentralRestServerAuthentication.authService.bind(this));
// Secured API

ADD
src/server/rest/router/SwaggerRouter.ts
+import express, { NextFunction, Request, Response } from 'express';
+
+import fs from 'fs';
+import global from '../../../types/GlobalType';
+import swaggerUi from 'swagger-ui-express';
+
+export const swaggerRouter = express.Router();
+
+const oasDocument = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/docs/open-api-standard.json`, 'utf8'));
+
+swaggerRouter.use('/', swaggerUi.serve);
+
+swaggerRouter.get('/', swaggerUi.setup(oasDocument));
+

MODIFY
src/server/rest/service/ConnectionService.ts
src/server/rest/service/ConnectionService.ts
@@ -7,7 +7,7 @@ import AppError from '../../../exception/AppError';
import Authorizations from '../../../authorization/Authorizations';
import ConnectionSecurity from './security/ConnectionSecurity';
import ConnectionStorage from '../../../storage/mongodb/ConnectionStorage';
-import ConnectionValidator from '../validation/ConnectionValidator';
+import ConnectionValidator from '../validator/ConnectionValidator';
import Constants from '../../../utils/Constants';
import Logging from '../../../utils/Logging';
import RefundFactory from '../../../integration/refund/RefundFactory';

MODIFY
src/server/rest/service/TenantService.ts
src/server/rest/service/TenantService.ts
@@ -17,7 +17,7 @@ import { StatusCodes } from 'http-status-codes';
import Tenant from '../../../types/Tenant';
import TenantSecurity from './security/TenantSecurity';
import TenantStorage from '../../../storage/mongodb/TenantStorage';
-import TenantValidator from '../validation/TenantValidation';
+import TenantValidator from '../validator/TenantValidation';
import UserStorage from '../../../storage/mongodb/UserStorage';
import Utils from '../../../utils/Utils';
import UtilsService from './UtilsService';

RENAME
src/server/rest/validation/ConnectionValidator.ts
src/server/rest/validator/ConnectionValidator.ts
RENAME
src/server/rest/validation/SchemaValidator.ts
src/server/rest/validator/SchemaValidator.ts
RENAME
src/server/rest/validation/TenantValidation.ts
src/server/rest/validator/TenantValidation.ts
Added Swagger docs