@hestia-earth/data-api 0.0.2-3 → 0.0.2-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/.dockerignore +0 -25
  3. package/.env.test +0 -7
  4. package/.eslintignore +0 -7
  5. package/.eslintrc.js +0 -11
  6. package/.gitlab-ci.yml +0 -128
  7. package/.mocharc.js +0 -8
  8. package/.nvmrc +0 -1
  9. package/.nycrc +0 -15
  10. package/Dockerfile +0 -17
  11. package/cleanup-docker.sh +0 -4
  12. package/commitlint.config.js +0 -1
  13. package/database/index.ts +0 -76
  14. package/database/migrations/001.do.init.sql +0 -53
  15. package/database/migrations/002.do.add-aggregated-sites.sql +0 -16
  16. package/database/migrations/003.do.add-generated-period-cols.sql +0 -7
  17. package/database/migrations/index.ts +0 -36
  18. package/database/seed/common.ts +0 -7
  19. package/database/seed/index.ts +0 -60
  20. package/database/seed/local/index.ts +0 -28
  21. package/database/seed/production/index.ts +0 -3
  22. package/database/seed/staging/index.ts +0 -5
  23. package/dev.ts +0 -3
  24. package/dist/aggregated-nodes/model/index.js +0 -11
  25. package/docker-compose.yml +0 -42
  26. package/envs/.master.env +0 -7
  27. package/envs/.staging.env +0 -7
  28. package/index.js +0 -3
  29. package/package.serverless.json +0 -21
  30. package/run-docker.sh +0 -14
  31. package/run-test.sh +0 -5
  32. package/scripts/run-lambda.ts +0 -10
  33. package/scripts/run-migrations.ts +0 -18
  34. package/scripts/run-resetdb.ts +0 -18
  35. package/scripts/run-seed.ts +0 -18
  36. package/serverless.yml +0 -101
  37. package/src/aggregated-nodes/model/index.ts +0 -37
  38. package/src/aggregated-nodes/routes/pg-get-filters.ts +0 -54
  39. package/src/aggregated-nodes/routes/pg-get.ts +0 -61
  40. package/src/aggregated-nodes/routes.spec.ts +0 -274
  41. package/src/aggregated-nodes/routes.ts +0 -56
  42. package/src/aggregated-nodes/services/pg-get-filters.ts +0 -62
  43. package/src/aggregated-nodes/services/pg-get.ts +0 -77
  44. package/src/app.spec.ts +0 -34
  45. package/src/app.ts +0 -59
  46. package/src/config.ts +0 -21
  47. package/src/cors.spec.ts +0 -32
  48. package/src/cors.ts +0 -7
  49. package/src/errors.spec.ts +0 -114
  50. package/src/errors.ts +0 -121
  51. package/src/index.spec.ts +0 -94
  52. package/src/index.ts +0 -14
  53. package/src/lambdas/sentry.ts +0 -12
  54. package/src/lambdas/update-aggregated-nodes/handler.spec.ts +0 -77
  55. package/src/lambdas/update-aggregated-nodes/handler.ts +0 -129
  56. package/src/logger.spec.ts +0 -20
  57. package/src/logger.ts +0 -45
  58. package/src/maintenance.spec.ts +0 -76
  59. package/src/maintenance.ts +0 -19
  60. package/src/models.ts +0 -1
  61. package/src/routes.ts +0 -8
  62. package/src/settings/model/index.ts +0 -21
  63. package/src/settings/routes/get.spec.ts +0 -33
  64. package/src/settings/routes/get.ts +0 -3
  65. package/src/settings/routes/update.spec.ts +0 -33
  66. package/src/settings/routes/update.ts +0 -5
  67. package/src/settings/routes.spec.ts +0 -75
  68. package/src/settings/routes.ts +0 -21
  69. package/src/settings/services/get.spec.ts +0 -62
  70. package/src/settings/services/get.ts +0 -18
  71. package/src/settings/services/update.spec.ts +0 -118
  72. package/src/settings/services/update.ts +0 -47
  73. package/src/slack.spec.ts +0 -42
  74. package/src/slack.ts +0 -17
  75. package/src/swagger/routes.ts +0 -57
  76. package/src/types/async-express-errors/index.d.ts +0 -1
  77. package/src/types/express/index.d.ts +0 -10
  78. package/src/utils/endpoint-wrapper.spec.ts +0 -80
  79. package/src/utils/endpoint-wrapper.ts +0 -16
  80. package/src/utils/middleware.spec.ts +0 -154
  81. package/src/utils/middleware.ts +0 -33
  82. package/test/Dockerfile +0 -13
  83. package/test/docker-compose.yml +0 -40
  84. package/test/fixtures/aggregated-nodes/get.ts +0 -196
  85. package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv +0 -5
  86. package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv.cycle.json +0 -458
  87. package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv.site.json +0 -182
  88. package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-impactassessment_pivoted.csv +0 -3
  89. package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-impactassessment_pivoted.csv.impactAssessment.json +0 -988
  90. package/test/fixtures/update-aggregated-nodes/abyssinianKaleStraw-impactassessment_pivoted.csv +0 -3
  91. package/test/fixtures/update-aggregated-nodes/cycle-missing-impactassessment_pivoted.csv +0 -3
  92. package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv +0 -5
  93. package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv.cycle.json +0 -584
  94. package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv.site.json +0 -212
  95. package/test/fixtures/update-aggregated-nodes/tomatoFruit-impactassessment_pivoted.csv +0 -3
  96. package/test/fixtures/update-aggregated-nodes/tomatoFruit-impactassessment_pivoted.csv.impactAssessment.json +0 -1002
  97. package/test/prepare.ts +0 -13
  98. package/test/utils.ts +0 -32
  99. package/tsconfig.build.json +0 -13
  100. package/tsconfig.dist.json +0 -14
  101. package/tsconfig.json +0 -42
  102. package/tsconfig.lambdas.json +0 -13
@@ -1,77 +0,0 @@
1
- import 'mocha';
2
- import sinon from 'sinon';
3
- import { of } from 'rxjs';
4
- import { expect } from 'chai';
5
- import * as s3 from '@hestia-earth/pipeline-utils/dist/s3';
6
- import { sortBy as _sortBy } from 'lodash';
7
-
8
- import { loadFixture, initDb } from '../../../test/utils';
9
- import { run } from './handler';
10
- import { query } from '../../../database';
11
-
12
- const stubs: sinon.SinonStub[] = [];
13
-
14
- const cycles = ['abyssinianKaleSeedWhole-cycle_pivoted.csv', 'tomatoFruit-cycle_pivoted.csv'];
15
-
16
- const impactAssessments = [
17
- 'abyssinianKaleSeedWhole-impactassessment_pivoted.csv',
18
- 'tomatoFruit-impactassessment_pivoted.csv'
19
- ];
20
-
21
- const keys = [
22
- ...cycles,
23
- ...impactAssessments,
24
- // abyssinianKaleStraw should be ignored as no ref to a cycle
25
- 'abyssinianKaleStraw-impactassessment_pivoted.csv',
26
- // hould be ignored as cycle.id refers to a non-existent cycle
27
- 'cycle-missing-impactassessment_pivoted.csv'
28
- ];
29
-
30
- const fixture = (fileName: string) => loadFixture(`update-aggregated-nodes/${fileName}`);
31
-
32
- const getJsonResults = (rows) =>
33
- rows
34
- .map((r) => r.jsonld_pivoted);
35
-
36
- describe('update-aggregated-nodes > run', () => {
37
- beforeEach(async () => {
38
- await initDb();
39
- stubs.push(sinon.stub(s3, 'listFolder').resolves(keys.map((Key) => ({ Key }))));
40
- stubs.push(sinon.stub(s3, 'getObject').callsFake(({ Key }) => of(fixture(Key)) as any));
41
- });
42
-
43
- afterEach(() => {
44
- stubs.forEach((stub) => stub.restore());
45
- });
46
-
47
- it('should save the pivoted csv files in the db', async () => {
48
- await run();
49
-
50
- const { rows: siteRows } = await query('SELECT * FROM aggregated_sites');
51
- const siteResult = _sortBy(getJsonResults(siteRows), '@id');
52
- const jsonPivotedSites = _sortBy(
53
- cycles.flatMap((key) => JSON.parse(fixture(`${key}.site.json`))),
54
- '@id'
55
- );
56
-
57
- expect(siteResult.sort()).to.deep.equal(jsonPivotedSites.sort());
58
-
59
- const { rows: cycleRows } = await query('SELECT * FROM aggregated_cycles');
60
- const cycleResult = _sortBy(getJsonResults(cycleRows), '@id');
61
- const jsonPivotedCycles = _sortBy(
62
- cycles.flatMap((key) => JSON.parse(fixture(`${key}.cycle.json`))),
63
- '@id'
64
- );
65
-
66
- expect(cycleResult).to.deep.equal(jsonPivotedCycles);
67
-
68
- const { rows: iaRows } = await query('SELECT * FROM aggregated_impact_assessments');
69
- const iaResult = _sortBy(getJsonResults(iaRows), '@id');
70
- const jsonPivotedIas = _sortBy(
71
- impactAssessments.flatMap((key) => JSON.parse(fixture(`${key}.impactAssessment.json`))),
72
- '@id'
73
- );
74
-
75
- expect(iaResult.sort()).to.deep.equal(jsonPivotedIas.sort());
76
- });
77
- });
@@ -1,129 +0,0 @@
1
- import { info, debug, timeSpent, listFolder, getObject, bucket } from '@hestia-earth/pipeline-utils';
2
- import csvtojson from 'csvtojson';
3
- import uniqBy from 'lodash.uniqby';
4
- import chunk from 'lodash.chunk';
5
-
6
- import { wrapHandler } from '../sentry';
7
- import { query } from '../../../database';
8
-
9
- // If a cycle or IA is missing its referenced site/cycle, it is out of date: omit
10
- // JOIN ensures only records with a valid fkey will be selected for insert
11
- const upsertCyclesQuery = `
12
- INSERT INTO aggregated_cycles (jsonld_pivoted)
13
- SELECT vals.jsonld_pivoted
14
- FROM (
15
- SELECT * FROM unnest($1::jsonb[], $2::text[])
16
- ) vals (jsonld_pivoted, site_hestia_id)
17
- JOIN aggregated_sites ON vals.site_hestia_id = aggregated_sites.hestia_id
18
- ON CONFLICT(hestia_id) DO UPDATE
19
- SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
20
- `;
21
-
22
- const upsertImpactAsessmentsQuery = `
23
- INSERT INTO aggregated_impact_assessments (jsonld_pivoted)
24
- SELECT vals.jsonld_pivoted
25
- FROM (
26
- SELECT * FROM unnest($1::jsonb[], $2::text[])
27
- ) vals (jsonld_pivoted, cycle_hestia_id)
28
- JOIN aggregated_cycles ON vals.cycle_hestia_id = aggregated_cycles.hestia_id
29
- ON CONFLICT(hestia_id) DO UPDATE
30
- SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
31
- `;
32
-
33
- const upsertSitesQuery = `
34
- INSERT INTO aggregated_sites (jsonld_pivoted)
35
- VALUES(unnest($1::jsonb[]))
36
- ON CONFLICT(hestia_id) DO UPDATE
37
- SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
38
- `;
39
-
40
- const foreignKeyGetters: { [key in AggregationTypes]: (node) => boolean } = {
41
- cycle: (cycle) => cycle.site['@id'],
42
- impactAssessment: (ia) => ia.cycle?.['@id'],
43
- site: (_site) => true
44
- };
45
-
46
- const queries: { [key in AggregationTypes]: string } = {
47
- cycle: upsertCyclesQuery,
48
- impactAssessment: upsertImpactAsessmentsQuery,
49
- site: upsertSitesQuery
50
- };
51
-
52
- enum AggregationTypes {
53
- cycle = 'cycle',
54
- impactAssessment = 'impactAssessment',
55
- site = 'site'
56
- }
57
-
58
- const typesInsertionOrder = [AggregationTypes.site, AggregationTypes.cycle, AggregationTypes.impactAssessment];
59
-
60
- const formatJson = (mapObj, type: AggregationTypes) => (json) =>
61
- mapObj.default(
62
- json[type],
63
- ((key, val) => {
64
- return val === '-' ? mapObj.mapObjectSkip : [key === 'id' ? '@id' : key, val];
65
- }) as any,
66
- { deep: true }
67
- );
68
-
69
- const getFormattedNodes = async (keys: string[]): Promise<{ [key in AggregationTypes]: any[] }> => {
70
- const mapObj = await import('map-obj');
71
-
72
- const jsons = (
73
- await Promise.all(
74
- keys.map(async (Key) => {
75
- const body = await getObject({ Key, Bucket: bucket }).toPromise();
76
- return await csvtojson({ delimiter: 'auto', checkType: true, ignoreEmpty: true }).fromString(body);
77
- })
78
- )
79
- ).flat();
80
-
81
- const formattedNodes = { cycle: [], site: [], impactAssessment: [] };
82
- // Each json represents a row of csv, with top-level keys 'cycle', 'impactAssessment', 'site'
83
- for (const type of typesInsertionOrder) {
84
- const formattedType = uniqBy(
85
- jsons
86
- .filter((json) => json[type])
87
- .map(formatJson(mapObj, type))
88
- .filter((node) => node['@id'] && foreignKeyGetters[type](node)),
89
- '@id'
90
- );
91
-
92
- formattedNodes[type].push(...formattedType);
93
- }
94
-
95
- return formattedNodes;
96
- };
97
-
98
- export const run = async () => {
99
- const folder = 'backups/aggregation';
100
- const allKeys = (await listFolder(folder)).map(({ Key }) => Key);
101
- debug('Found', allKeys.length, 'files in', folder, 'folder on bucket', bucket);
102
-
103
- // cycles must be processed/inserted before impact assessments because of fkey constraints
104
- const pivoted = [
105
- ...allKeys.filter((Key) => Key.endsWith('cycle_pivoted.csv')),
106
- ...allKeys.filter((Key) => Key.endsWith('impactassessment_pivoted.csv'))
107
- ];
108
- debug(pivoted.length, 'pivoted files to process');
109
-
110
- const chunks = chunk(pivoted, 500);
111
- for (const value of chunks) {
112
- const nodes = await getFormattedNodes(value);
113
- for (const type of typesInsertionOrder) {
114
- const params =
115
- type === AggregationTypes.site ? [nodes[type]] : [nodes[type], nodes[type].map(foreignKeyGetters[type])];
116
- await query(queries[type], params);
117
- }
118
- }
119
- };
120
-
121
- export const updateAggregatedNodes = async () => {
122
- const functionName = 'updateAggregatedNodes';
123
- info(functionName);
124
- const now = new Date();
125
- await run();
126
- info(`Finished ${functionName} in ${timeSpent(now)}ms`);
127
- };
128
-
129
- export const updateAggregatedNodesHandler = wrapHandler(updateAggregatedNodes);
@@ -1,20 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { logger, stream } from './logger';
6
-
7
- describe('logger', () => {
8
- it('should have 1 transport', () => {
9
- expect(logger.transports.length).to.equal(1);
10
- });
11
- });
12
-
13
- describe('stream', () => {
14
- it('should use logger info', () => {
15
- const stub: sinon.SinonStub = sinon.stub(logger, 'info');
16
- stream.write('message');
17
- expect(stub.calledWith('message')).to.equal(true);
18
- stub.restore();
19
- });
20
- });
package/src/logger.ts DELETED
@@ -1,45 +0,0 @@
1
- import { createLogger, format, transports } from 'winston';
2
-
3
- const logFilename = process.env.LOG_FILENAME;
4
-
5
- const options = {
6
- file: {
7
- level: (process.env.LOG_LEVEL || 'info').toLowerCase(),
8
- filename: logFilename,
9
- handleExceptions: true,
10
- maxsize: 5242880, // 5MB
11
- maxFiles: 5,
12
- colorize: false,
13
- format: format.combine(
14
- format.timestamp(),
15
- format.metadata({ fillExcept: ['message', 'level', 'timestamp'] }),
16
- format.json()
17
- )
18
- },
19
- console: {
20
- level: 'debug',
21
- handleExceptions: true,
22
- format: format.combine(
23
- format.colorize(),
24
- format.timestamp(),
25
- format.printf((info) => {
26
- return `${info.timestamp} - ${info.level}: ${info.message}${
27
- Object.keys(info.metadata).length === 0 ? '' : ' ' + JSON.stringify(info.metadata)
28
- }`;
29
- })
30
- )
31
- }
32
- };
33
-
34
- export const logger = createLogger({
35
- format: options.file.format,
36
- transports: [...(logFilename ? [new transports.File(options.file)] : []), new transports.Console(options.console)],
37
- exitOnError: false
38
- });
39
-
40
- export const stream = {
41
- write: (message) => {
42
- // use the 'info' log level so the output will be picked up by both transports (file and console)
43
- logger.info(message);
44
- }
45
- };
@@ -1,76 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { maintenance, clearCachedMaintenanceSetting } from './maintenance';
6
- import * as getService from './settings/services/get';
7
- import { updateAll } from './settings/services/update';
8
- import { initDb } from '../test/utils';
9
- import { SettingKey } from './settings/model';
10
- import * as updateService from './settings/services/update';
11
-
12
- let stubs: sinon.SinonStub[] = [];
13
-
14
- describe('maintenance', () => {
15
- let nextStub: sinon.SinonStub;
16
- let isEnabledStub: sinon.SinonStub;
17
-
18
- beforeEach(() => {
19
- stubs = [];
20
- nextStub = sinon.stub();
21
- clearCachedMaintenanceSetting();
22
- });
23
-
24
- afterEach(() => {
25
- stubs.forEach((stub) => stub.restore());
26
- });
27
-
28
- describe('maintenance', () => {
29
- const req: any = {};
30
-
31
- describe('with maintenance setting cache', () => {
32
- beforeEach(async () => {
33
- await initDb();
34
- stubs.push((isEnabledStub = sinon.stub(getService, 'isEnabled').callThrough()));
35
- stubs.push(sinon.stub(updateService, 'postMessage').resolves());
36
- });
37
-
38
- it('returns the cached value on second call', async () => {
39
- await maintenance(req, null, nextStub);
40
- await maintenance(req, null, nextStub);
41
- expect(isEnabledStub.calledOnce).to.equal(true);
42
- expect(nextStub.args).to.deep.equal([[], []]);
43
- });
44
-
45
- it('refreshes cache when maintenance setting updated', async () => {
46
- await maintenance(req, null, nextStub);
47
- await updateAll({ [SettingKey.maintenanceEnabled]: false });
48
- await maintenance(req, null, nextStub);
49
- expect(isEnabledStub.calledTwice).to.equal(true);
50
- expect(nextStub.args).to.deep.equal([[], []]);
51
- });
52
- });
53
-
54
- describe('maintenance enabled', () => {
55
- beforeEach(() => {
56
- stubs.push(sinon.stub(getService, 'isEnabled').resolves(true));
57
- });
58
-
59
- it('should throw an error', async () => {
60
- await maintenance(req, null, nextStub);
61
- expect(nextStub.calledWithExactly()).to.equal(false);
62
- });
63
- });
64
-
65
- describe('maintenance disabled', () => {
66
- beforeEach(() => {
67
- stubs.push(sinon.stub(getService, 'isEnabled').resolves(false));
68
- });
69
-
70
- it('should NOT throw an error', async () => {
71
- await maintenance(req, null, nextStub);
72
- expect(nextStub.calledWithExactly()).to.equal(true);
73
- });
74
- });
75
- });
76
- });
@@ -1,19 +0,0 @@
1
- import { Request, Response, NextFunction } from 'express';
2
- import { HestiaError, Errors, StatusCodes } from './errors';
3
- import { SettingKey } from './settings/model';
4
- import { isEnabled } from './settings/services/get';
5
-
6
- const cachedValue: { enabled?: boolean } = {};
7
-
8
- export const maintenance = async (_req: Request, _res: Response, next: NextFunction) => {
9
- if (!cachedValue.hasOwnProperty('enabled')) {
10
- cachedValue.enabled = await isEnabled(SettingKey.maintenanceEnabled);
11
- }
12
- return cachedValue.enabled
13
- ? next(new HestiaError(Errors.UnderMaintenance, {}, StatusCodes.ServiceUnavailable))
14
- : next();
15
- };
16
-
17
- export const clearCachedMaintenanceSetting = () => {
18
- delete cachedValue.enabled;
19
- };
package/src/models.ts DELETED
@@ -1 +0,0 @@
1
- export * from './aggregated-nodes/model';
package/src/routes.ts DELETED
@@ -1,8 +0,0 @@
1
- import { Express } from 'express';
2
- import aggregatedNodes from './aggregated-nodes/routes';
3
-
4
- const routes = (app: Express) => {
5
- app.use('/aggregated-nodes', aggregatedNodes());
6
- };
7
-
8
- export default routes;
@@ -1,21 +0,0 @@
1
- import { query } from '../../../database';
2
- import { clearCachedMaintenanceSetting } from '../../maintenance';
3
-
4
- export enum SettingKey {
5
- maintenanceEnabled = 'maintenanceEnabled'
6
- }
7
-
8
- export const getSetting = (setting: SettingKey) =>
9
- query('SELECT setting, active, metadata FROM settings WHERE setting = $1', [setting]);
10
-
11
- export const getAllSettings = () => query('SELECT setting, active, metadata FROM settings');
12
-
13
- export const setSetting = async (setting: { key: SettingKey; value: boolean; metadata?: any }) => {
14
- const result = await query('UPDATE settings SET active = $2, metadata = (metadata || $3) WHERE setting = $1', [
15
- setting.key,
16
- setting.value,
17
- setting.metadata || {}
18
- ]);
19
- clearCachedMaintenanceSetting();
20
- return result;
21
- };
@@ -1,33 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { get } from './get';
6
- import * as serviceGet from '../services/get';
7
-
8
- let stubs: sinon.SinonStub[] = [];
9
-
10
- describe('settings > routes', () => {
11
- beforeEach(() => {
12
- stubs = [];
13
- });
14
-
15
- afterEach(() => {
16
- stubs.forEach((stub) => stub.restore());
17
- });
18
-
19
- describe('get', () => {
20
- describe('get', () => {
21
- let getStub: sinon.SinonStub;
22
-
23
- beforeEach(() => {
24
- stubs.push((getStub = sinon.stub(serviceGet, 'getAll').resolves({})));
25
- });
26
-
27
- it('should call get', async () => {
28
- await get();
29
- expect(getStub.called).to.equal(true);
30
- });
31
- });
32
- });
33
- });
@@ -1,3 +0,0 @@
1
- import { getAll } from '../services/get';
2
-
3
- export const get = () => getAll();
@@ -1,33 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { update } from './update';
6
- import * as serviceUpdate from '../services/update';
7
-
8
- let stubs: sinon.SinonStub[] = [];
9
-
10
- describe('settings > routes', () => {
11
- beforeEach(() => {
12
- stubs = [];
13
- });
14
-
15
- afterEach(() => {
16
- stubs.forEach((stub) => stub.restore());
17
- });
18
-
19
- describe('update', () => {
20
- describe('update', () => {
21
- let setStub: sinon.SinonStub;
22
-
23
- beforeEach(() => {
24
- stubs.push((setStub = sinon.stub(serviceUpdate, 'updateAll').resolves([])));
25
- });
26
-
27
- it('should call update', async () => {
28
- await update({} as any);
29
- expect(setStub.called).to.equal(true);
30
- });
31
- });
32
- });
33
- });
@@ -1,5 +0,0 @@
1
- import { Request } from 'express';
2
-
3
- import { updateAll } from '../services/update';
4
-
5
- export const update = ({ body }: Request) => updateAll(body);
@@ -1,75 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
- import express from 'express';
5
- import supertest from 'supertest';
6
- import bodyParser from 'body-parser';
7
-
8
- import router from './routes';
9
- import { errorHandler } from '../errors';
10
- import { initDb } from '../../test/utils';
11
- import * as update from './services/update';
12
-
13
- let stubs: sinon.SinonStub[] = [];
14
-
15
- const app = express();
16
-
17
- app.use(bodyParser.json());
18
- app.use(router());
19
- app.use(errorHandler);
20
-
21
- let postMessageStub: sinon.SinonStub;
22
-
23
- describe('routes', () => {
24
- beforeEach(() => {
25
- stubs = [];
26
- stubs.push((postMessageStub = sinon.stub(update, 'postMessage').resolves()));
27
- });
28
-
29
- afterEach(() => {
30
- stubs.forEach((stub) => stub.restore());
31
- });
32
-
33
- describe('settings', () => {
34
- beforeEach(async () => {
35
- await initDb();
36
- });
37
-
38
- describe('GET /', () => {
39
- it('returns all settings', async () => {
40
- const result = await supertest(app).get('/').set('Accept', 'application/json');
41
- expect(result.body).to.deep.equal({ maintenanceEnabled: false });
42
- });
43
- });
44
-
45
- describe('PUT /', () => {
46
- describe('with correct token', () => {
47
- it('updates the passed settings and calls postMessage', async () => {
48
- await supertest(app)
49
- .put('/')
50
- .set('Accept', 'application/json')
51
- .set('x-access-token', 'dummy')
52
- .type('json')
53
- .send({ maintenanceEnabled: true })
54
- .expect(200);
55
-
56
- const result = await supertest(app).get('/').set('Accept', 'application/json');
57
- expect(result.body).to.deep.equal({ maintenanceEnabled: true });
58
- expect(postMessageStub.called).to.equal(true);
59
- });
60
- });
61
-
62
- describe('with incorrect token', () => {
63
- it('returns Unauthorized error', async () => {
64
- await supertest(app)
65
- .put('/')
66
- .set('Accept', 'application/json')
67
- .set('x-access-token', '')
68
- .type('json')
69
- .send({ maintenanceEnabled: true })
70
- .expect(401);
71
- });
72
- });
73
- });
74
- });
75
- });
@@ -1,21 +0,0 @@
1
- import { Request, Response, NextFunction, Router } from 'express';
2
-
3
- import { endpointWrapper } from '../utils/endpoint-wrapper';
4
- import { get } from './routes/get';
5
- import { update } from './routes/update';
6
- import { HestiaError, Errors } from '../errors';
7
-
8
- export const canWriteSettings = (req: Request, _res: Response, next: NextFunction) =>
9
- req.headers['x-access-token'] === process.env.WRITE_SETTINGS_SECRET
10
- ? next()
11
- : next(new HestiaError(Errors.Unauthorized));
12
-
13
- export default () => {
14
- const router = Router();
15
-
16
- router.get('/', endpointWrapper(get));
17
-
18
- router.put('/', canWriteSettings, endpointWrapper(update));
19
-
20
- return router;
21
- };
@@ -1,62 +0,0 @@
1
- import chai from 'chai';
2
- import chaiAsPromised from 'chai-as-promised';
3
- chai.use(chaiAsPromised);
4
- const { expect } = chai;
5
-
6
- import * as sinon from 'sinon';
7
- import 'mocha';
8
-
9
- import { fixtures, initDb } from '../../../test/utils';
10
-
11
- import { get, isEnabled } from './get';
12
- import * as specs from './get';
13
- import { SettingKey } from '../model';
14
-
15
- let stubs: sinon.SinonStub[] = [];
16
-
17
- describe('settings > services', () => {
18
- beforeEach(() => {
19
- stubs = [];
20
- });
21
-
22
- afterEach(() => {
23
- stubs.forEach((stub) => stub.restore());
24
- });
25
-
26
- describe('get', () => {
27
- beforeEach(async () => {
28
- await initDb();
29
- });
30
-
31
- describe('get', () => {
32
- describe('setting not found', () => {
33
- it('should throw an error', () => {
34
- return expect(get('random_key' as any)).to.be.rejected;
35
- });
36
- });
37
-
38
- describe('setting found', () => {
39
- it('should return the setting', async () => {
40
- const {
41
- rows: [setting]
42
- } = await get(SettingKey.maintenanceEnabled);
43
- expect(setting).to.deep.equal(fixtures.settings[0]);
44
- });
45
- });
46
- });
47
-
48
- describe('isEnabled', () => {
49
- const active = true;
50
-
51
- describe('setting exists', () => {
52
- beforeEach(() => {
53
- stubs.push(sinon.stub(specs, 'get').resolves({ rows: [{ setting: 'key', active }] } as any));
54
- });
55
-
56
- it('should return the setting value', async () => {
57
- expect(await isEnabled('key' as any)).to.equal(active);
58
- });
59
- });
60
- });
61
- });
62
- });
@@ -1,18 +0,0 @@
1
- import { SettingKey, getSetting, getAllSettings } from '../model/';
2
-
3
- const loadAllSettings = async () => {
4
- const { rows } = await getAllSettings();
5
- const value = rows.reduce((prev, setting) => ({ ...prev, [setting.setting]: setting.active }), {});
6
- return value;
7
- };
8
-
9
- export const getAll = () => loadAllSettings();
10
-
11
- export const get = (key: SettingKey) => getSetting(key);
12
-
13
- export const isEnabled = async (key: SettingKey) => {
14
- const {
15
- rows: [setting]
16
- } = await get(key);
17
- return setting?.active;
18
- };