@hestia-earth/data-api 0.0.2-2 → 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.
- package/package.json +3 -2
- package/.dockerignore +0 -25
- package/.env.test +0 -7
- package/.eslintignore +0 -7
- package/.eslintrc.js +0 -11
- package/.gitlab-ci.yml +0 -125
- package/.mocharc.js +0 -8
- package/.nvrm +0 -1
- package/.nycrc +0 -15
- package/Dockerfile +0 -17
- package/cleanup-docker.sh +0 -4
- package/commitlint.config.js +0 -1
- package/database/index.ts +0 -76
- package/database/migrations/001.do.init.sql +0 -53
- package/database/migrations/002.do.add-aggregated-sites.sql +0 -16
- package/database/migrations/003.do.add-generated-period-cols.sql +0 -7
- package/database/migrations/index.ts +0 -36
- package/database/seed/common.ts +0 -7
- package/database/seed/index.ts +0 -55
- package/database/seed/local/index.ts +0 -28
- package/database/seed/production/index.ts +0 -3
- package/database/seed/staging/index.ts +0 -5
- package/database/seed/test/index.ts +0 -28
- package/dev.ts +0 -3
- package/dist/aggregated-nodes/model/index.js +0 -11
- package/docker-compose.yml +0 -42
- package/envs/.master.env +0 -7
- package/envs/.staging.env +0 -7
- package/index.js +0 -3
- package/run-docker.sh +0 -14
- package/run-test.sh +0 -5
- package/scripts/run-lambda.ts +0 -10
- package/scripts/run-migrations.ts +0 -18
- package/scripts/run-resetdb.ts +0 -18
- package/scripts/run-seed.ts +0 -18
- package/serverless.yml +0 -76
- package/src/aggregated-nodes/model/index.ts +0 -37
- package/src/aggregated-nodes/routes/pg-get-filters.ts +0 -44
- package/src/aggregated-nodes/routes/pg-get.ts +0 -50
- package/src/aggregated-nodes/routes.spec.ts +0 -242
- package/src/aggregated-nodes/routes.ts +0 -56
- package/src/aggregated-nodes/services/pg-get-filters.ts +0 -52
- package/src/aggregated-nodes/services/pg-get.ts +0 -77
- package/src/app.spec.ts +0 -34
- package/src/app.ts +0 -59
- package/src/config.ts +0 -21
- package/src/cors.spec.ts +0 -32
- package/src/cors.ts +0 -7
- package/src/errors.spec.ts +0 -114
- package/src/errors.ts +0 -121
- package/src/index.spec.ts +0 -94
- package/src/index.ts +0 -14
- package/src/lambdas/sentry.ts +0 -12
- package/src/lambdas/update-aggregated-nodes/handler.spec.ts +0 -86
- package/src/lambdas/update-aggregated-nodes/handler.ts +0 -141
- package/src/logger.spec.ts +0 -20
- package/src/logger.ts +0 -45
- package/src/maintenance.spec.ts +0 -76
- package/src/maintenance.ts +0 -19
- package/src/models.ts +0 -1
- package/src/routes.ts +0 -8
- package/src/settings/model/index.ts +0 -21
- package/src/settings/routes/get.spec.ts +0 -33
- package/src/settings/routes/get.ts +0 -3
- package/src/settings/routes/update.spec.ts +0 -33
- package/src/settings/routes/update.ts +0 -5
- package/src/settings/routes.spec.ts +0 -75
- package/src/settings/routes.ts +0 -21
- package/src/settings/services/get.spec.ts +0 -62
- package/src/settings/services/get.ts +0 -18
- package/src/settings/services/update.spec.ts +0 -118
- package/src/settings/services/update.ts +0 -47
- package/src/slack.spec.ts +0 -42
- package/src/slack.ts +0 -17
- package/src/swagger/routes.ts +0 -57
- package/src/types/async-express-errors/index.d.ts +0 -1
- package/src/types/express/index.d.ts +0 -10
- package/src/utils/endpoint-wrapper.spec.ts +0 -80
- package/src/utils/endpoint-wrapper.ts +0 -16
- package/src/utils/middleware.spec.ts +0 -154
- package/src/utils/middleware.ts +0 -33
- package/test/Dockerfile +0 -13
- package/test/docker-compose.yml +0 -40
- package/test/fixtures/aggregated-nodes/get.ts +0 -184
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv +0 -5
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv.cycle.json +0 -458
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-cycle_pivoted.csv.site.json +0 -182
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-impactassessment_pivoted.csv +0 -3
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleSeedWhole-impactassessment_pivoted.csv.impactAssessment.json +0 -988
- package/test/fixtures/update-aggregated-nodes/abyssinianKaleStraw-impactassessment_pivoted.csv +0 -3
- package/test/fixtures/update-aggregated-nodes/cycle-missing-impactassessment_pivoted.csv +0 -3
- package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv +0 -5
- package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv.cycle.json +0 -584
- package/test/fixtures/update-aggregated-nodes/tomatoFruit-cycle_pivoted.csv.site.json +0 -212
- package/test/fixtures/update-aggregated-nodes/tomatoFruit-impactassessment_pivoted.csv +0 -3
- package/test/fixtures/update-aggregated-nodes/tomatoFruit-impactassessment_pivoted.csv.impactAssessment.json +0 -1002
- package/test/prepare.ts +0 -13
- package/test/utils.ts +0 -33
- package/tsconfig.build.json +0 -13
- package/tsconfig.dist.json +0 -14
- package/tsconfig.json +0 -42
- package/tsconfig.lambdas.json +0 -13
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { info, debug, error, timeSpent, listFolder, getObject } from '@hestia-earth/pipeline-utils';
|
|
2
|
-
import { aggregationFolder } from '@hestia-earth/api';
|
|
3
|
-
import csvtojson from 'csvtojson';
|
|
4
|
-
import uniqBy from 'lodash.uniqby';
|
|
5
|
-
import chunk from 'lodash.chunk';
|
|
6
|
-
|
|
7
|
-
import { wrapHandler } from '../sentry';
|
|
8
|
-
import { query } from '../../../database';
|
|
9
|
-
|
|
10
|
-
const bucket = process.env.BUCKET_DATA;
|
|
11
|
-
|
|
12
|
-
// If a cycle or IA is missing its referenced site/cycle, it is out of date: omit
|
|
13
|
-
// JOIN ensures only records with a valid fkey will be selected for insert
|
|
14
|
-
const upsertCyclesQuery = `
|
|
15
|
-
INSERT INTO aggregated_cycles (jsonld_pivoted)
|
|
16
|
-
SELECT vals.jsonld_pivoted
|
|
17
|
-
FROM (
|
|
18
|
-
SELECT * FROM unnest($1::jsonb[], $2::text[])
|
|
19
|
-
) vals (jsonld_pivoted, site_hestia_id)
|
|
20
|
-
JOIN aggregated_sites ON vals.site_hestia_id = aggregated_sites.hestia_id
|
|
21
|
-
ON CONFLICT(hestia_id) DO UPDATE
|
|
22
|
-
SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
|
|
23
|
-
`;
|
|
24
|
-
|
|
25
|
-
const upsertImpactAsessmentsQuery = `
|
|
26
|
-
INSERT INTO aggregated_impact_assessments (jsonld_pivoted)
|
|
27
|
-
SELECT vals.jsonld_pivoted
|
|
28
|
-
FROM (
|
|
29
|
-
SELECT * FROM unnest($1::jsonb[], $2::text[])
|
|
30
|
-
) vals (jsonld_pivoted, cycle_hestia_id)
|
|
31
|
-
JOIN aggregated_cycles ON vals.cycle_hestia_id = aggregated_cycles.hestia_id
|
|
32
|
-
ON CONFLICT(hestia_id) DO UPDATE
|
|
33
|
-
SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
const upsertSitesQuery = `
|
|
37
|
-
INSERT INTO aggregated_sites (jsonld_pivoted)
|
|
38
|
-
VALUES(unnest($1::jsonb[]))
|
|
39
|
-
ON CONFLICT(hestia_id) DO UPDATE
|
|
40
|
-
SET jsonld_pivoted = EXCLUDED.jsonld_pivoted;
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
const foreignKeyGetters: { [key in AggregationTypes]: (node) => boolean } = {
|
|
44
|
-
cycle: (cycle) => cycle.site['@id'],
|
|
45
|
-
impactAssessment: (ia) => ia.cycle?.['@id'],
|
|
46
|
-
site: (_site) => true
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const queries: { [key in AggregationTypes]: string } = {
|
|
50
|
-
cycle: upsertCyclesQuery,
|
|
51
|
-
impactAssessment: upsertImpactAsessmentsQuery,
|
|
52
|
-
site: upsertSitesQuery
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
enum AggregationTypes {
|
|
56
|
-
cycle = 'cycle',
|
|
57
|
-
impactAssessment = 'impactAssessment',
|
|
58
|
-
site = 'site'
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const typesInsertionOrder = [AggregationTypes.site, AggregationTypes.cycle, AggregationTypes.impactAssessment];
|
|
62
|
-
|
|
63
|
-
const formatJson = (mapObj, type: AggregationTypes) => (json) =>
|
|
64
|
-
mapObj.default(
|
|
65
|
-
json[type],
|
|
66
|
-
((key, val) => {
|
|
67
|
-
return val === '-' ? mapObj.mapObjectSkip : [key === 'id' ? '@id' : key, val];
|
|
68
|
-
}) as any,
|
|
69
|
-
{ deep: true }
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const getFormattedNodes = async (keys: string[]): Promise<{ [key in AggregationTypes]: any[] }> => {
|
|
73
|
-
const mapObj = await import('map-obj');
|
|
74
|
-
|
|
75
|
-
const jsons = (
|
|
76
|
-
await Promise.all(
|
|
77
|
-
keys.map(async (Key) => {
|
|
78
|
-
const body = await getObject({ Key, Bucket: bucket }).toPromise();
|
|
79
|
-
return await csvtojson({ delimiter: 'auto', checkType: true, ignoreEmpty: true }).fromString(body);
|
|
80
|
-
})
|
|
81
|
-
)
|
|
82
|
-
).flat();
|
|
83
|
-
|
|
84
|
-
const formattedNodes = { cycle: [], site: [], impactAssessment: [] };
|
|
85
|
-
// Each json represents a row of csv, with top-level keys 'cycle', 'impactAssessment', 'site'
|
|
86
|
-
for (const type of typesInsertionOrder) {
|
|
87
|
-
const formattedType = uniqBy(
|
|
88
|
-
jsons
|
|
89
|
-
.filter((json) => json[type])
|
|
90
|
-
.map(formatJson(mapObj, type))
|
|
91
|
-
.filter((node) => node['@id'] && foreignKeyGetters[type](node)),
|
|
92
|
-
'@id'
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
formattedNodes[type].push(...formattedType);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return formattedNodes;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const run = async () => {
|
|
102
|
-
const folder = `backups/${aggregationFolder}`;
|
|
103
|
-
const allKeys = (await listFolder(folder, bucket)).map(({ Key }) => Key);
|
|
104
|
-
debug('Found', allKeys.length, 'files in', folder, 'folder on bucket', bucket);
|
|
105
|
-
|
|
106
|
-
// cycles must be processed/inserted before impact assessments because of fkey constraints
|
|
107
|
-
const pivoted = [
|
|
108
|
-
...allKeys.filter((Key) => Key.endsWith('cycle_pivoted.csv')),
|
|
109
|
-
...allKeys.filter((Key) => Key.endsWith('impactassessment_pivoted.csv'))
|
|
110
|
-
];
|
|
111
|
-
debug(pivoted.length, 'pivoted files to process');
|
|
112
|
-
|
|
113
|
-
const chunks = chunk(pivoted, 500);
|
|
114
|
-
for (const value of chunks) {
|
|
115
|
-
const nodes = await getFormattedNodes(value);
|
|
116
|
-
for (const type of typesInsertionOrder) {
|
|
117
|
-
const params =
|
|
118
|
-
type === AggregationTypes.site ? [nodes[type]] : [nodes[type], nodes[type].map(foreignKeyGetters[type])];
|
|
119
|
-
await query(queries[type], params);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export const updateAggregatedNodes = async () => {
|
|
125
|
-
const functionName = 'updateAggregatedNodes';
|
|
126
|
-
info(functionName);
|
|
127
|
-
const now = new Date();
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
await run();
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
error(err);
|
|
134
|
-
throw err;
|
|
135
|
-
}
|
|
136
|
-
finally {
|
|
137
|
-
info(`Finished ${functionName} in ${timeSpent(now)}ms`);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export const updateAggregatedNodesHandler = wrapHandler(updateAggregatedNodes);
|
package/src/logger.spec.ts
DELETED
|
@@ -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
|
-
};
|
package/src/maintenance.spec.ts
DELETED
|
@@ -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
|
-
});
|
package/src/maintenance.ts
DELETED
|
@@ -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,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,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,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
|
-
});
|
package/src/settings/routes.ts
DELETED
|
@@ -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
|
-
};
|