Added sitearea getMany/getOne/getImage schemas
MODIFY
src/assets/schemas/common/common.json
src/assets/schemas/common/common.json
"language": {
"type": "string",
"sanitize": "mongo"
+ },
+ "LocLongitude": {
+ "type": "number",
+ "format": "longitude",
+ "sanitize": "mongo"
+ },
+ "LocLatitude": {
+ "type": "number",
+ "format": "latitude",
+ "sanitize": "mongo"
+ },
+ "LocMaxDistanceMeters": {
+ "type": "number",
+ "sanitize": "mongo",
+ "exclusiveMinimum": 0
}
}
}

ADD
src/assets/schemas/site-area/site-area.json
+{
+ "$id": "site-area",
+ "definitions": {
+ "id": {
+ "$ref": "common#/definitions/id"
+ },
+ "name": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "address": {
+ "$ref": "common#/definitions/address"
+ },
+ "maximumPower": {
+ "type": "number",
+ "sanitize": "mongo",
+ "minimum": 0
+ },
+ "numberOfPhases": {
+ "type": "number",
+ "sanitize": "mongo",
+ "enum": [1, 3]
+ },
+ "voltage": {
+ "type": "number",
+ "sanitize": "mongo",
+ "enum": [110, 230]
+ },
+ "smartCharging": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "accessControl": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "siteID": {
+ "$ref": "common#/definitions/id"
+ },
+ "image": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+}

MODIFY
src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json
src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json
"sanitize": "mongo"
},
"LocLongitude": {
- "type": "number",
- "format": "longitude",
- "sanitize": "mongo"
+ "$ref": "common#/definitions/LocLongitude"
},
"LocLatitude": {
- "type": "number",
- "format": "latitude",
- "sanitize": "mongo"
+ "$ref": "common#/definitions/LocLatitude"
},
"LocMaxDistanceMeters": {
- "type": "number",
- "sanitize": "mongo",
- "exclusiveMinimum": 0
+ "$ref": "common#/definitions/LocMaxDistanceMeters"
},
"Limit": {
"$ref": "common#/definitions/limit"

MODIFY
src/assets/server/rest/v1/schemas/company/companies-get.json
src/assets/server/rest/v1/schemas/company/companies-get.json
"sanitize": "mongo"
},
"LocLongitude": {
- "type": "number",
- "format": "longitude",
- "sanitize": "mongo"
+ "$ref": "common#/definitions/LocLongitude"
},
"LocLatitude": {
- "type": "number",
- "format": "latitude",
- "sanitize": "mongo"
+ "$ref": "common#/definitions/LocLatitude"
},
"LocMaxDistanceMeters": {
- "type": "number",
- "sanitize": "mongo",
- "exclusiveMinimum": 0
+ "$ref": "common#/definitions/LocMaxDistanceMeters"
},
"Limit": {
"$ref": "common#/definitions/limit"

ADD
src/assets/server/rest/v1/schemas/site-area/site-area-get-image.json
+{
+ "$id": "site-area-get-image",
+ "title": "Get Site Area Image Request",
+ "type": "object",
+ "properties": {
+ "ID": {
+ "$ref": "common#/definitions/id"
+ },
+ "TenantID": {
+ "$ref": "common#/definitions/id"
+ }
+ },
+ "required": [
+ "ID",
+ "TenantID"
+ ]
+}

ADD
src/assets/server/rest/v1/schemas/site-area/site-area-get.json
+{
+ "$id": "site-area-get",
+ "title": "Get Site Area Request",
+ "type": "object",
+ "properties": {
+ "ID": {
+ "$ref": "common#/definitions/id"
+ },
+ "WithSite": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "WithChargingStations": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ }
+ },
+ "required": [
+ "ID"
+ ]
+}

ADD
src/assets/server/rest/v1/schemas/site-area/site-areas-get.json
+{
+ "$id": "site-areas-get",
+ "title": "Get Site Areas Request",
+ "type": "object",
+ "properties": {
+ "WithSite": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "WithChargingStations": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "WithAvailableChargers": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "SiteID": {
+ "$ref": "common#/definitions/id"
+ },
+ "CompanyID": {
+ "$ref": "common#/definitions/id"
+ },
+ "Issuer": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "LocLongitude": {
+ "$ref": "common#/definitions/LocLongitude"
+ },
+ "LocLatitude": {
+ "$ref": "common#/definitions/LocLatitude"
+ },
+ "LocMaxDistanceMeters": {
+ "$ref": "common#/definitions/LocMaxDistanceMeters"
+ },
+ "Search": {
+ "$ref": "common#/definitions/search"
+ },
+ "SortFields": {
+ "$ref": "common#/definitions/sortFields"
+ },
+ "Limit": {
+ "$ref": "common#/definitions/limit"
+ },
+ "Skip": {
+ "$ref": "common#/definitions/skip"
+ },
+ "OnlyRecordCount": {
+ "$ref": "common#/definitions/onlyRecordCount"
+ },
+ "ProjectFields": {
+ "$ref": "common#/definitions/projectFields"
+ }
+ },
+ "required": [
+ "Limit"
+ ]
+}

MODIFY
src/server/rest/v1/service/SiteAreaService.ts
src/server/rest/v1/service/SiteAreaService.ts
@@ -18,6 +18,7 @@ import SiteArea from '../../../../types/SiteArea';
import { SiteAreaDataResult } from '../../../../types/DataResult';
import SiteAreaSecurity from './security/SiteAreaSecurity';
import SiteAreaStorage from '../../../../storage/mongodb/SiteAreaStorage';
+import SiteAreaValidator from '../validator/SiteAreaValidator';
import SmartChargingFactory from '../../../../integration/smart-charging/SmartChargingFactory';
import { TenantComponents } from '../../../../types/Tenant';
import TenantStorage from '../../../../storage/mongodb/TenantStorage';
@@ -118,7 +119,7 @@ export default class SiteAreaService {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ORGANIZATION,
Action.DELETE, Entity.SITE_AREA, MODULE_NAME, 'handleDeleteSiteArea');
// Filter request
- const siteAreaID = SiteAreaSecurity.filterSiteAreaRequestByID(req.query);
+ const siteAreaID = SiteAreaValidator.getInstance().validateSiteAreaGetReq(req.query).ID;
UtilsService.assertIdIsProvided(action, siteAreaID, MODULE_NAME, 'handleDeleteSiteArea', req.user);
// Check and Get Site Area
const siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(
@@ -144,7 +145,7 @@ export default class SiteAreaService {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ORGANIZATION,
Action.READ, Entity.SITE_AREA, MODULE_NAME, 'handleGetSiteArea');
// Filter request
- const filteredRequest = SiteAreaSecurity.filterSiteAreaRequest(req.query);
+ const filteredRequest = SiteAreaValidator.getInstance().validateSiteAreaGetReq(req.query);
// Check and Get Site Area
const siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(
req.tenant, req.user, filteredRequest.ID, Action.READ, action, null, {
@@ -159,9 +160,7 @@ export default class SiteAreaService {
public static async handleGetSiteAreaImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
- const filteredRequest = SiteAreaSecurity.filterSiteImageRequest(req.query);
- // Check mandatory fields
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetSiteAreaImage', req.user);
+ const filteredRequest = SiteAreaValidator.getInstance().validateSiteAreaGetImageReq(req.query);
// Get it
const siteAreaImage = await SiteAreaStorage.getSiteAreaImage(
await TenantStorage.getTenant(filteredRequest.TenantID), filteredRequest.ID);
@@ -188,7 +187,14 @@ export default class SiteAreaService {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ORGANIZATION,
Action.LIST, Entity.SITE_AREA, MODULE_NAME, 'handleGetSiteAreas');
// Filter
- const filteredRequest = SiteAreaSecurity.filterSiteAreasRequest(req.query);
+ const filteredRequest = SiteAreaValidator.getInstance().validateSiteAreasGetReq(req.query);
+ // Create GPS Coordinates
+ if (filteredRequest.LocLongitude && filteredRequest.LocLatitude) {
+ filteredRequest.LocCoordinates = [
+ Utils.convertToFloat(filteredRequest.LocLongitude),
+ Utils.convertToFloat(filteredRequest.LocLatitude)
+ ];
+ }
// Check dynamic auth
const authorizationSiteAreasFilter = await AuthorizationService.checkAndGetSiteAreasAuthorizations(
req.tenant, req.user, filteredRequest);
@@ -213,7 +219,7 @@ export default class SiteAreaService {
{
limit: filteredRequest.Limit,
skip: filteredRequest.Skip,
- sort: filteredRequest.SortFields,
+ sort: UtilsService.httpSortFieldsToMongoDB(filteredRequest.SortFields),
onlyRecordCount: filteredRequest.OnlyRecordCount
},
authorizationSiteAreasFilter.projectFields

ADD
src/server/rest/v1/validator/SiteAreaValidator.ts
+import { HttpSiteAreaImageRequest, HttpSiteAreaRequest, HttpSiteAreasRequest } from '../../../../types/requests/HttpSiteAreaRequest';
+
+import Schema from '../../../../types/validator/Schema';
+import SchemaValidator from '../../../../validator/SchemaValidator';
+import fs from 'fs';
+import global from '../../../../types/GlobalType';
+
+export default class SiteAreaValidator extends SchemaValidator {
+ private static instance: SiteAreaValidator | undefined;
+ private siteAreasGet: Schema;
+ private siteAreaGet: Schema;
+ private siteAreaGetImage: Schema;
+
+
+ private constructor() {
+ super('SiteAreaValidator');
+ this.siteAreasGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/site-area/site-areas-get.json`, 'utf8'));
+ this.siteAreaGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/site-area/site-area-get.json`, 'utf8'));
+ this.siteAreaGetImage = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/site-area/site-area-get-image.json`, 'utf8'));
+ }
+
+ public static getInstance(): SiteAreaValidator {
+ if (!SiteAreaValidator.instance) {
+ SiteAreaValidator.instance = new SiteAreaValidator();
+ }
+ return SiteAreaValidator.instance;
+ }
+
+ public validateSiteAreasGetReq(data: Record<string, unknown>): HttpSiteAreasRequest {
+ return this.validate(this.siteAreasGet, data);
+ }
+
+ public validateSiteAreaGetReq(data: Record<string, unknown>): HttpSiteAreaRequest {
+ return this.validate(this.siteAreaGet, data);
+ }
+
+ public validateSiteAreaGetImageReq(data: Record<string, unknown>): HttpSiteAreaImageRequest {
+ return this.validate(this.siteAreaGetImage, data);
+ }
+}

MODIFY
src/types/requests/HttpSiteAreaRequest.ts
src/types/requests/HttpSiteAreaRequest.ts
@@ -15,6 +15,8 @@ export interface HttpSiteAreasRequest extends HttpDatabaseRequest {
WithSite?: boolean;
WithChargeBoxes?: boolean;
WithAvailableChargers: boolean;
+ LocLongitude?: number;
+ LocLatitude?: number;
LocCoordinates?: number[];
LocMaxDistanceMeters?: number;
}

MODIFY
src/validator/SchemaValidator.ts
src/validator/SchemaValidator.ts
@@ -30,6 +30,7 @@ export default class SchemaValidator {
private static companySchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/company/company.json`, 'utf8'));
private static ocpiEndpointSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/ocpi/ocpi-endpoint.json`, 'utf8'));
private static pricingDefinitionSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/pricing/pricing-definition.json`, 'utf8'));
+ private static siteAreasSchema: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/site-area/site-area.json`, 'utf8'));
private readonly ajv: Ajv;
constructor(readonly moduleName: string,
@@ -65,6 +66,7 @@ export default class SchemaValidator {
this.ajv.addSchema(SchemaValidator.companySchema);
this.ajv.addSchema(SchemaValidator.ocpiEndpointSchema);
this.ajv.addSchema(SchemaValidator.pricingDefinitionSchema);
+ this.ajv.addSchema(SchemaValidator.siteAreasSchema);
}
protected validate(schema: Schema, data: Record<string, unknown>): any {

Added sitearea getMany/getOne/getImage schemas