Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support a few domains (500) #283

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ilc/common/wrapWithCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a required parameter.

} = 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__'}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass name to hash function. Don't do a concatenation after

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now we would use hash fn even if we don't have args


if (localStorage.getItem(hash) === null || JSON.parse(localStorage.getItem(hash)).cachedAt < now - cacheForSeconds) {
if (cacheResolutionPromise[hash] !== undefined) {
Expand Down
3 changes: 2 additions & 1 deletion ilc/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

Expand Down
3 changes: 2 additions & 1 deletion ilc/server/errorHandler/ErrorHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
43 changes: 41 additions & 2 deletions ilc/server/registry/Registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = class Registry {
#cacheHeated = {
config: false,
template: false,
routerDomains: false,
};

/**
Expand All @@ -30,6 +31,7 @@ module.exports = class Registry {

const getConfigMemo = wrapFetchWithCache(this.#getConfig, {
cacheForSeconds: 5,
name: 'registry_getConfig',
});

this.getConfig = async (options) => {
Expand All @@ -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;
}

Expand All @@ -53,6 +70,7 @@ module.exports = class Registry {
await Promise.all([
this.getConfig(),
this.getTemplate('500'),
this.getRouterDomains(),
]);

this.#logger.info('Registry preheated successfully!');
Expand Down Expand Up @@ -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;
Expand Down
9 changes: 8 additions & 1 deletion registry/client/src/routerDomains/Edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
TextInput,
required,
TextField,
ReferenceInput,
SelectInput,
} from 'react-admin'; // eslint-disable-line import/no-unresolved

const Title = ({ record }) => {
Expand All @@ -19,7 +21,12 @@ const InputForm = ({ mode = 'edit', ...props }) => {
? <TextField source="id" />
: null}

<TextInput source="domainName" fullWidth validate={required()} />
<TextInput source="domainName" fullWidth validate={required()} />
<ReferenceInput reference="template"
source="template500"
label="Template of 500 error">
<SelectInput resettable optionText="name" helperText="Default template name is '500'" />
</ReferenceInput>
</SimpleForm>
);
};
Expand Down
1 change: 1 addition & 0 deletions registry/client/src/routerDomains/List.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const PostList = props => {
<Datagrid rowClick="edit" optimized>
<TextField source="id" sortable={false} />
<TextField source="domainName" sortable={false} />
<TextField source="template500" sortable={false} emptyText="-" />
<ListActionsToolbar>
<EditButton />
</ListActionsToolbar>
Expand Down
2 changes: 1 addition & 1 deletion registry/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(authMw));

app.use(errorHandler);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export async function up(knex: Knex): Promise<any> {
return knex.schema.createTable('router_domains', table => {
table.increments('id');
table.string('domainName', 255).notNullable();
table.string('template500', 50).nullable().references('templates.name')
});
}

Expand Down
12 changes: 9 additions & 3 deletions registry/server/routerDomains/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -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(),
});
18 changes: 10 additions & 8 deletions registry/server/routerDomains/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import express from 'express';
import express, { RequestHandler } from 'express';

import getRouterDomains from './getRouterDomains';
import getAllRouterDomains from './getAllRouterDomains';
import updateRouterDomains from './updateRouterDomains';
import createRouterDomains from './createRouterDomains';
import deleteRouterDomains from './deleteRouterDomains';

const routerDomainsRouter = express.Router();
export default (authMw: RequestHandler) => {
const routerDomainsRouter = express.Router();

routerDomainsRouter.get('/', ...getAllRouterDomains);
routerDomainsRouter.post('/', ...createRouterDomains);
routerDomainsRouter.get('/:id', ...getRouterDomains);
routerDomainsRouter.put('/:id', ...updateRouterDomains);
routerDomainsRouter.delete('/:id', ...deleteRouterDomains);
routerDomainsRouter.get('/', ...getAllRouterDomains);
routerDomainsRouter.post('/', authMw, ...createRouterDomains);
routerDomainsRouter.get('/:id', authMw, ...getRouterDomains);
routerDomainsRouter.put('/:id', authMw, ...updateRouterDomains);
routerDomainsRouter.delete('/:id', authMw, ...deleteRouterDomains);

export default routerDomainsRouter;
return routerDomainsRouter;
};
54 changes: 51 additions & 3 deletions registry/tests/routerDomains.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -56,6 +67,46 @@ describe(`Tests ${example.url}`, () => {
}
});

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);
}
});

describe('Authentication / Authorization', () => {
it('should deny access w/o authentication', async () => {
await requestWithAuth.post(example.url)
Expand Down Expand Up @@ -159,9 +210,6 @@ describe(`Tests ${example.url}`, () => {

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);
});
Expand Down
32 changes: 32 additions & 0 deletions registry/tests/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down