Save tenants using schema
MODIFY
src/assets/schemas/common/common.json
src/assets/schemas/common/common.json
"pattern": "^$|^[0-9a-fA-F]{24}$",
"sanitize": "mongo"
},
+ "objectId": {
+ "type": "string",
+ "pattern": "^$|^[0-9a-fA-F]{24}$",
+ "sanitize": "mongo",
+ "customType": "objectId"
+ },
"ids": {
"type": "string",
"pattern": "^$|^([0-9a-fA-F]{24}\\|?)*$",
"type": "number",
"sanitize": "mongo",
"exclusiveMinimum": 0
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "sanitize": "mongo",
+ "transform": ["toLowerCase"]
}
}
}

MODIFY
src/assets/schemas/tenant/tenant.json
src/assets/schemas/tenant/tenant.json
{
"$id": "tenant",
- "definitions": {
+ "type": "object",
+ "properties": {
"id": {
"$ref": "common#/definitions/id"
},
"minLength": 1,
"maxLength": 100,
"sanitize": "mongo"
+ },
+ "email": {
+ "$ref": "common#/definitions/email"
+ },
+ "subdomain": {
+ "$ref": "common#/definitions/subdomain"
+ },
+ "components": {
+ "$ref": "tenant-components#/definitions/components"
+ },
+ "address": {
+ "$ref": "common#/definitions/address"
+ },
+ "logo": {
+ "$ref": "common#/definitions/logo"
}
- }
+ },
+ "required": [
+ "name",
+ "email",
+ "subdomain"
+ ]
}

MODIFY
src/assets/server/rest/v1/schemas/tenant/tenant-create.json
src/assets/server/rest/v1/schemas/tenant/tenant-create.json
"type": "object",
"properties": {
"id": {
- "$ref": "tenant#/definitions/id"
+ "$ref": "tenant#/properties/id"
},
"name": {
- "$ref": "tenant#/definitions/name"
+ "$ref": "tenant#/properties/name"
},
"email": {
"$ref": "user#/definitions/email"

MODIFY
src/assets/server/rest/v1/schemas/tenant/tenant-get.json
src/assets/server/rest/v1/schemas/tenant/tenant-get.json
"type": "object",
"properties": {
"ID": {
- "$ref": "tenant#/definitions/id"
+ "$ref": "tenant#/properties/id"
},
"ProjectFields": {
"$ref": "common#/definitions/projectFields"

MODIFY
src/assets/server/rest/v1/schemas/tenant/tenant-logo-get.json
src/assets/server/rest/v1/schemas/tenant/tenant-logo-get.json
"type": "object",
"properties": {
"ID": {
- "$ref": "tenant#/definitions/id"
+ "$ref": "tenant#/properties/id"
},
"Subdomain": {
"type": "string",

MODIFY
src/assets/server/rest/v1/schemas/tenant/tenant-update.json
src/assets/server/rest/v1/schemas/tenant/tenant-update.json
"type": "object",
"properties": {
"id": {
- "$ref": "tenant#/definitions/id"
+ "$ref": "tenant#/properties/id"
},
"name": {
- "$ref": "tenant#/definitions/name"
+ "$ref": "tenant#/properties/name"
},
"email": {
"$ref": "user#/definitions/email"

MODIFY
src/storage/mongodb/TenantStorage.ts
src/storage/mongodb/TenantStorage.ts
@@ -8,6 +8,7 @@ import DatabaseUtils from './DatabaseUtils';
import DbParams from '../../types/database/DbParams';
import Logging from '../../utils/Logging';
import { ObjectId } from 'mongodb';
+import TenantValidatorStorage from './validator/TenantValidatorStorage';
import Utils from '../../utils/Utils';
const MODULE_NAME = 'TenantStorage';
@@ -39,13 +40,6 @@ export default class TenantStorage {
public static async saveTenant(tenantToSave: Partial<Tenant>, saveLogo = true): Promise<string> {
const startTime = Logging.traceDatabaseRequestStart();
- if (!tenantToSave.id && !tenantToSave.name) {
- throw new BackendError({
- module: MODULE_NAME,
- method: 'saveTenant',
- message: 'Tenant has no ID and no Name'
- });
- }
const tenantFilter: any = {};
// Build Request
if (tenantToSave.id) {
@@ -53,30 +47,7 @@ export default class TenantStorage {
} else {
tenantFilter._id = new ObjectId();
}
- // Properties to save
- // eslint-disable-next-line prefer-const
- let tenantMDB = {
- _id: tenantFilter._id,
- name: tenantToSave.name,
- email: tenantToSave.email,
- subdomain: tenantToSave.subdomain,
- components: tenantToSave.components ? tenantToSave.components : {},
- };
- if (tenantToSave.address) {
- Object.assign(tenantMDB, {
- address: {
- address1: tenantToSave.address.address1,
- address2: tenantToSave.address.address2,
- postalCode: tenantToSave.address.postalCode,
- city: tenantToSave.address.city,
- department: tenantToSave.address.department,
- region: tenantToSave.address.region,
- country: tenantToSave.address.country,
- coordinates: Utils.hasValidGpsCoordinates(tenantToSave.address.coordinates) ? tenantToSave.address.coordinates.map(
- (coordinate) => Utils.convertToFloat(coordinate)) : [],
- }
- });
- }
+ const tenantMDB = TenantValidatorStorage.getInstance().validateTenant(tenantToSave);
DatabaseUtils.addLastChangedCreatedProps(tenantMDB, tenantToSave);
// Modify
await global.database.getCollection<Tenant>(Constants.DEFAULT_TENANT, 'tenants').findOneAndUpdate(
@@ -87,11 +58,11 @@ export default class TenantStorage {
if (saveLogo) {
// Modify
await global.database.getCollection<any>(Constants.DEFAULT_TENANT, 'tenantlogos').findOneAndUpdate(
- { '_id': tenantMDB._id },
- { $set: { logo: tenantToSave.logo } },
+ { '_id': tenantFilter._id },
+ { $set: { logo: tenantMDB.logo } },
{ upsert: true });
}
- await Logging.traceDatabaseRequestEnd(Constants.DEFAULT_TENANT_OBJECT, MODULE_NAME, 'saveTenant', startTime, { tenant: tenantMDB, logo: tenantToSave.logo });
+ await Logging.traceDatabaseRequestEnd(Constants.DEFAULT_TENANT_OBJECT, MODULE_NAME, 'saveTenant', startTime, { tenant: tenantMDB, logo: tenantMDB.logo });
return tenantFilter._id.toString();
}

ADD
src/storage/mongodb/validator/TenantValidatorStorage.ts
+import Schema from '../../../types/validator/Schema';
+import SchemaValidator from '../../../validator/SchemaValidator';
+import Tenant from '../../../types/Tenant';
+import fs from 'fs';
+import global from '../../../types/GlobalType';
+
+export default class TenantValidatorStorage extends SchemaValidator {
+ private static instance: TenantValidatorStorage | null = null;
+ private tenant: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/schemas/tenant/tenant.json`, 'utf8'));
+
+ private constructor() {
+ super('TenantValidatorStorage', {
+ strict: false, // When 'true', it fails with anyOf required fields: https://github.com/ajv-validator/ajv/issues/1571
+ allErrors: true,
+ removeAdditional: true, // 'all' fails with anyOf documents: Manually added 'additionalProperties: false' in schema due filtering of data in anyOf/oneOf/allOf array (it's standard): https://github.com/ajv-validator/ajv/issues/1784
+ allowUnionTypes: true,
+ coerceTypes: true,
+ verbose: true,
+ });
+ }
+
+ public static getInstance(): TenantValidatorStorage {
+ if (!TenantValidatorStorage.instance) {
+ TenantValidatorStorage.instance = new TenantValidatorStorage();
+ }
+ return TenantValidatorStorage.instance;
+ }
+
+ public validateTenant(data: any): Tenant {
+ return this.validate(this.tenant, data);
+ }
+}

Save tenants using schema