Skip to content

Commit

Permalink
feat: support a few domains (500 template)
Browse files Browse the repository at this point in the history
  • Loading branch information
nightnei committed Apr 15, 2021
1 parent 955ba4a commit ed612be
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 41 deletions.
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
} = 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) {
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);

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(),
});
80 changes: 49 additions & 31 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,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);
}
});
});

Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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);
});
});
});
});
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

0 comments on commit ed612be

Please sign in to comment.