diff --git a/ilc/common/wrapWithCache.js b/ilc/common/wrapWithCache.js
index 06f0c44a..ed1213e7 100644
--- a/ilc/common/wrapWithCache.js
+++ b/ilc/common/wrapWithCache.js
@@ -6,13 +6,14 @@ errors.WrapWithCacheError = extendError('WrapWithCacheError');
const wrapWithCache = (localStorage, logger, createHash = hashFn) => (fn, cacheParams = {}) => {
const {
cacheForSeconds = 60,
+ name = '', // "hash" of returned value is based only on arguments, so with the help "name" we can add prefix to hash
} = cacheParams;
const cacheResolutionPromise = {};
return (...args) => {
const now = Math.floor(Date.now() / 1000);
- const hash = args.length > 0 ? createHash(JSON.stringify(args)) : '__null__';
+ const hash = `${name ? name + '__' : ''}${args.length > 0 ? createHash(JSON.stringify(args)) : '__null__'}`;
if (localStorage.getItem(hash) === null || JSON.parse(localStorage.getItem(hash)).cachedAt < now - cacheForSeconds) {
if (cacheResolutionPromise[hash] !== undefined) {
diff --git a/ilc/server/app.js b/ilc/server/app.js
index 800caf14..e7318f49 100644
--- a/ilc/server/app.js
+++ b/ilc/server/app.js
@@ -43,7 +43,8 @@ module.exports = (registryService, pluginManager) => {
app.register(require('./ping'));
app.get('/_ilc/api/v1/registry/template/:templateName', async (req, res) => {
- const data = await registryService.getTemplate(req.params.templateName);
+ const currentDomain = req.hostname;
+ const data = await registryService.getTemplate(req.params.templateName, currentDomain);
res.status(200).send(data.data.content);
});
diff --git a/ilc/server/errorHandler/ErrorHandler.js b/ilc/server/errorHandler/ErrorHandler.js
index faf8c0cb..461526e4 100644
--- a/ilc/server/errorHandler/ErrorHandler.js
+++ b/ilc/server/errorHandler/ErrorHandler.js
@@ -51,7 +51,8 @@ module.exports = class ErrorHandler {
errorId
});
- let data = await this.#registryService.getTemplate('500');
+ const currentDomain = req.hostname;
+ let data = await this.#registryService.getTemplate('500', currentDomain);
data = data.data.content.replace('%ERRORID%', `Error ID: ${errorId}`);
nres.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
diff --git a/ilc/server/registry/Registry.js b/ilc/server/registry/Registry.js
index c6c1a03c..9e878e37 100644
--- a/ilc/server/registry/Registry.js
+++ b/ilc/server/registry/Registry.js
@@ -13,6 +13,7 @@ module.exports = class Registry {
#cacheHeated = {
config: false,
template: false,
+ routerDomains: false,
};
/**
@@ -30,6 +31,7 @@ module.exports = class Registry {
const getConfigMemo = wrapFetchWithCache(this.#getConfig, {
cacheForSeconds: 5,
+ name: 'registry_getConfig',
});
this.getConfig = async (options) => {
@@ -38,13 +40,28 @@ module.exports = class Registry {
return res;
};
- this.getTemplate = wrapFetchWithCache(this.#getTemplate, {
+ this.getRouterDomains = wrapFetchWithCache(this.#getRouterDomains, {
cacheForSeconds: 30,
+ name: 'registry_routerDomains',
});
+
+ const getTemplateMemo = wrapFetchWithCache(this.#getTemplate, {
+ cacheForSeconds: 30,
+ });
+
+ this.getTemplate = async (templateName, forDomain) => {
+ if (templateName === '500' && forDomain) {
+ const routerDomains = await this.getRouterDomains();
+ const redefined500 = routerDomains.data.find(item => item.domainName === forDomain)?.template500;
+ templateName = redefined500 || templateName;
+ }
+
+ return await getTemplateMemo(templateName);
+ };
}
async preheat() {
- if (this.#cacheHeated.template && this.#cacheHeated.config) {
+ if (this.#cacheHeated.template && this.#cacheHeated.config && this.#cacheHeated.routerDomains) {
return;
}
@@ -53,6 +70,7 @@ module.exports = class Registry {
await Promise.all([
this.getConfig(),
this.getTemplate('500'),
+ this.getRouterDomains(),
]);
this.#logger.info('Registry preheated successfully!');
@@ -102,6 +120,27 @@ module.exports = class Registry {
return res.data;
};
+ #getRouterDomains = async () => {
+ this.#logger.debug('Calling get routerDomains registry endpoint...');
+
+ const url = urljoin(this.#address, 'api/v1/router_domains');
+ let res;
+ try {
+ res = await axios.get(url, { responseType: 'json' });
+ } catch (e) {
+ throw new errors.RegistryError({
+ message: `Error while requesting routerDomains from registry`,
+ cause: e,
+ data: {
+ requestedUrl: url
+ }
+ });
+ }
+
+ this.#cacheHeated.routerDomains = true;
+ return res.data;
+ };
+
#filterConfig = (config, filter) => {
if (!filter || !Object.keys(filter).length) {
return config;
diff --git a/registry/client/src/routerDomains/Edit.js b/registry/client/src/routerDomains/Edit.js
index cd0124b7..196ae939 100644
--- a/registry/client/src/routerDomains/Edit.js
+++ b/registry/client/src/routerDomains/Edit.js
@@ -6,6 +6,8 @@ import {
TextInput,
required,
TextField,
+ ReferenceInput,
+ SelectInput,
} from 'react-admin'; // eslint-disable-line import/no-unresolved
const Title = ({ record }) => {
@@ -19,7 +21,12 @@ const InputForm = ({ mode = 'edit', ...props }) => {
?
: null}
-
+
+
+
+
);
};
diff --git a/registry/client/src/routerDomains/List.js b/registry/client/src/routerDomains/List.js
index 377ee4f0..9a7e7652 100644
--- a/registry/client/src/routerDomains/List.js
+++ b/registry/client/src/routerDomains/List.js
@@ -51,6 +51,7 @@ const PostList = props => {
+
diff --git a/registry/server/app.ts b/registry/server/app.ts
index fa70b3ae..cfca063b 100644
--- a/registry/server/app.ts
+++ b/registry/server/app.ts
@@ -40,7 +40,7 @@ export default (withAuth: boolean = true) => {
app.use('/api/v1/auth_entities', authMw, routes.authEntities);
app.use('/api/v1/versioning', authMw, routes.versioning);
app.use('/api/v1/settings', routes.settings(authMw));
- app.use('/api/v1/router_domains', authMw, routes.routerDomains);
+ app.use('/api/v1/router_domains', routes.routerDomains);
app.use(errorHandler);
diff --git a/registry/server/migrations/20210405164831_router_domains.ts b/registry/server/migrations/20210405164831_router_domains.ts
index 45d73f12..6817ae88 100644
--- a/registry/server/migrations/20210405164831_router_domains.ts
+++ b/registry/server/migrations/20210405164831_router_domains.ts
@@ -5,6 +5,7 @@ export async function up(knex: Knex): Promise {
return knex.schema.createTable('router_domains', table => {
table.increments('id');
table.string('domainName', 255).notNullable();
+ table.string('template500', 50).nullable().references('templates.name')
});
}
diff --git a/registry/server/routerDomains/interfaces/index.ts b/registry/server/routerDomains/interfaces/index.ts
index 5a1866e3..0e6c28f3 100644
--- a/registry/server/routerDomains/interfaces/index.ts
+++ b/registry/server/routerDomains/interfaces/index.ts
@@ -1,18 +1,24 @@
import Joi from 'joi';
+import { templateNameSchema } from '../../templates/interfaces';
export default interface RouterDomains {
id: number,
domainName: string,
+ template500?: string,
};
export const routerDomainIdSchema = Joi.string().trim().required();
-const routerDomainNameSchema = Joi.string().trim().min(1);
+const commonRouterDomainsSchema = {
+ domainName: Joi.string().trim().min(1),
+ template500: templateNameSchema.allow(null),
+};
export const partialRouterDomainsSchema = Joi.object({
- domainName: routerDomainNameSchema,
+ ...commonRouterDomainsSchema,
});
export const routerDomainsSchema = Joi.object({
- domainName: routerDomainNameSchema.required(),
+ ...commonRouterDomainsSchema,
+ domainName: commonRouterDomainsSchema.domainName.required(),
});
diff --git a/registry/tests/routerDomains.spec.ts b/registry/tests/routerDomains.spec.ts
index 3c332fc4..80cc1343 100644
--- a/registry/tests/routerDomains.spec.ts
+++ b/registry/tests/routerDomains.spec.ts
@@ -29,6 +29,17 @@ describe(`Tests ${example.url}`, () => {
.expect(422, '"domainName" must be a string');
});
+ it('should not create record with non-existed template500', async () => {
+ const response = await request.post(example.url)
+ .send({
+ ...example.correct,
+ template500: 'nonExistedTemplate',
+ })
+ .expect(500);
+
+ expect(response.text).to.include('Internal server error occurred.');
+ });
+
it('should successfully create record', async () => {
let routerDomainsId;
@@ -56,12 +67,44 @@ describe(`Tests ${example.url}`, () => {
}
});
- describe('Authentication / Authorization', () => {
- it('should deny access w/o authentication', async () => {
- await requestWithAuth.post(example.url)
- .send(example.correct)
- .expect(401);
- });
+ it('should successfully create record with template for 500', async () => {
+ const templateName = 'testTemplate500';
+ let routerDomainsId;
+
+ const exampleWithTemplate500 = {
+ ...example.correct,
+ template500: templateName,
+ };
+
+ try {
+ await request.post('/api/v1/template/')
+ .send({
+ name: templateName,
+ content: 'ncTestTemplateContent'
+ });
+
+ const responseCreation = await request.post(example.url)
+ .send(exampleWithTemplate500)
+ .expect(200)
+
+ routerDomainsId = responseCreation.body.id;
+
+ expect(responseCreation.body).deep.equal({
+ id: routerDomainsId,
+ ...exampleWithTemplate500,
+ });
+
+ const responseFetching = await request.get(example.url + routerDomainsId)
+ .expect(200);
+
+ expect(responseFetching.body).deep.equal({
+ id: routerDomainsId,
+ ...exampleWithTemplate500,
+ });
+ } finally {
+ routerDomainsId && await request.delete(example.url + routerDomainsId);
+ await request.delete('/api/v1/template/' + templateName);
+ }
});
});
@@ -156,16 +199,6 @@ describe(`Tests ${example.url}`, () => {
await Promise.all(routerDomainsList.map(item => request.delete(example.url + item.id)));
}
});
-
- describe('Authentication / Authorization', () => {
- it('should deny access w/o authentication', async () => {
- await requestWithAuth.get(example.url)
- .expect(401);
-
- await requestWithAuth.get(example.url + 123)
- .expect(401);
- });
- });
});
describe('Update', () => {
@@ -213,14 +246,6 @@ describe(`Tests ${example.url}`, () => {
routerDomainsId && await request.delete(example.url + routerDomainsId);
}
});
-
- describe('Authentication / Authorization', () => {
- it('should deny access w/o authentication', async () => {
- await requestWithAuth.put(example.url + 123)
- .send(example.updated)
- .expect(401);
- });
- });
});
describe('Delete', () => {
@@ -286,12 +311,5 @@ describe(`Tests ${example.url}`, () => {
await request.delete(example.url + response.body.id)
.expect(204, '');
});
-
- describe('Authentication / Authorization', () => {
- it('should deny access w/o authentication', async () => {
- await requestWithAuth.delete(example.url + 123)
- .expect(401);
- });
- });
});
});
diff --git a/registry/tests/templates.spec.ts b/registry/tests/templates.spec.ts
index 5ff56ae9..d58e81dc 100644
--- a/registry/tests/templates.spec.ts
+++ b/registry/tests/templates.spec.ts
@@ -292,6 +292,38 @@ describe(`Tests ${example.url}`, () => {
expect(response.body).deep.equal({});
});
+ it('should successfully delete record if doesn\'t have any reference from foreign (routerDomains -> template500) to current primary key', async () => {
+ let routerDomainsId;
+
+ try {
+ await request.post(example.url).send(example.correct).expect(200);
+
+ const responseRouterDomains = await request.post('/api/v1/router_domains/')
+ .send({
+ domainName: 'domainNameCorrect',
+ template500: example.correct.name,
+ })
+ .expect(200)
+
+ routerDomainsId = responseRouterDomains.body.id;
+
+ const response = await request.delete(example.url + example.correct.name)
+ .expect(500);
+ expect(response.text).to.include('Internal server error occurred.');
+
+ await request.delete('/api/v1/router_domains/' + routerDomainsId);
+
+ await request.delete(example.url + example.correct.name)
+ .expect(204, '');
+
+ await request.get(example.url + example.correct.name)
+ .expect(404, 'Not found');
+ } finally {
+ routerDomainsId && await request.delete('/api/v1/router_domains/' + routerDomainsId);
+ await request.delete(example.url + example.correct.name)
+ }
+ });
+
it('should successfully delete record', async () => {
await request.post(example.url).send(example.correct).expect(200);