@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,62 +0,0 @@
1
- import { getFilterClauses } from './pg-get';
2
- import { query as doQuery } from '../../../database';
3
- import { IFilter, IFilters } from '../model';
4
-
5
- const getFiltersQuery = (filter: IFilter): [string, any[]] => {
6
- const { filterClauses, params } = getFilterClauses(filter);
7
- return [
8
- `
9
- SELECT
10
- COUNT(DISTINCT c) as "cycleCount",
11
- COUNT(DISTINCT ia) AS "impactAssessmentCount",
12
- COALESCE(ARRAY_AGG(DISTINCT products ORDER BY products) FILTER (WHERE products IS NOT NULL), '{}') as products,
13
- COALESCE(
14
- ARRAY_AGG(DISTINCT c.generated_period ORDER BY c.generated_period) FILTER (WHERE c.generated_period IS NOT NULL),
15
- '{}'
16
- ) as periods,
17
- COALESCE(ARRAY_AGG(DISTINCT practices ORDER BY practices) FILTER (WHERE practices IS NOT NULL), '{}') as practices,
18
- COALESCE(
19
- ARRAY_AGG(
20
- DISTINCT (c.jsonld_pivoted->>'aggregatedQualityScore')::integer
21
- ORDER BY (c.jsonld_pivoted->>'aggregatedQualityScore')::integer
22
- ) FILTER (WHERE c.jsonld_pivoted->>'aggregatedQualityScore' is NOT NULL),
23
- '{}'
24
- ) AS "minAggregatedQualityScore",
25
- COALESCE(ARRAY_AGG(
26
- DISTINCT s.jsonld_pivoted['country']->>'@id'
27
- ORDER BY s.jsonld_pivoted['country']->>'@id'
28
- ) FILTER (WHERE s.jsonld_pivoted['country']->>'@id' IS NOT NULL), '{}') AS regions,
29
- COALESCE(
30
- ARRAY_AGG(
31
- DISTINCT c.jsonld_pivoted->>'defaultMethodClassification'
32
- ORDER BY c.jsonld_pivoted->>'defaultMethodClassification'
33
- ) FILTER (WHERE c.jsonld_pivoted->>'defaultMethodClassification' IS NOT NULL),
34
- '{}'
35
- ) AS "defaultMethodClassifications"
36
- FROM aggregated_cycles AS c
37
- LEFT JOIN
38
- jsonb_object_keys(c.jsonld_pivoted['products']) as products on TRUE
39
- LEFT JOIN
40
- jsonb_object_keys(c.jsonld_pivoted['practices']) as practices on TRUE
41
- LEFT JOIN aggregated_impact_assessments AS ia
42
- ON c.hestia_id = ia.cycle_hestia_id
43
- AND ${filterClauses.products.iaFilter}
44
- JOIN aggregated_sites AS s
45
- ON c.site_hestia_id = s.hestia_id
46
- AND ${filterClauses.regions}
47
- WHERE ${filterClauses.practices}
48
- AND ${filterClauses.defaultMethodClassifications}
49
- AND ${filterClauses.products.cycleFilter}
50
- AND ${filterClauses.minAggregatedQualityScore}
51
- AND ${filterClauses.periods}
52
- `,
53
- params
54
- ];
55
- };
56
-
57
- export const applyFilters = async ({ filter }: { filter: IFilter; query?: string }) => {
58
- // TODO: handle query later
59
- const [pgQuery, params] = getFiltersQuery(filter);
60
- const res = await doQuery<IFilters>(pgQuery, params);
61
- return res?.rows[0];
62
- };
@@ -1,77 +0,0 @@
1
- import { partition as _partition } from 'lodash';
2
- import { query as doQuery } from '../../../database';
3
- import { AggregatedNodeRow, IFilter } from '../model';
4
-
5
- type FilterClauseFunc = (args: { idx: number; exclude: boolean }) => string | Record<string, string>;
6
-
7
- const filterClauseMapping: Record<keyof IFilter, FilterClauseFunc> = {
8
- periods: ({ idx, exclude = false }) => (exclude ? 'TRUE' : `c.generated_period = ANY($${idx}::text[])`),
9
- // IA aggregatedQualityScore is always equal to that of cycle
10
- minAggregatedQualityScore: ({ idx, exclude = false }) =>
11
- exclude ? 'TRUE' : `(c.jsonld_pivoted->>'aggregatedQualityScore')::integer >= $${idx}`,
12
- practices: ({ idx, exclude = false }) => (exclude ? 'TRUE' : `c.jsonld_pivoted['practices'] ?| $${idx}::text[]`),
13
- products: ({ idx, exclude = false }) => ({
14
- iaFilter: exclude ? 'TRUE' : `ia.jsonld_pivoted['product']['term']->>'@id' = ANY($${idx}::text[])`,
15
- cycleFilter: exclude ? 'TRUE' : `c.jsonld_pivoted['products'] ?| $${idx}::text[]`
16
- }),
17
- // country field also holds values like 'Northern Europe'
18
- regions: ({ idx, exclude = false }) =>
19
- exclude ? 'TRUE' : `s.jsonld_pivoted['country']->>'@id' = ANY($${idx}::text[])`,
20
- defaultMethodClassifications: ({ idx, exclude = false }) =>
21
- exclude ? 'TRUE' : `c.jsonld_pivoted->>'defaultMethodClassification' = ANY($${idx}::text[])`
22
- };
23
-
24
- export const getFilterClauses = (filter: IFilter) => {
25
- const filterClauses: Partial<Record<keyof IFilter, Record<string, string>>> = {};
26
- const params = [];
27
-
28
- const [included, excluded] = _partition(Object.keys(filterClauseMapping), (key) => filter[key]);
29
-
30
- included.forEach((key, idx) => {
31
- filterClauses[key] = filterClauseMapping[key]({ idx: idx + 1 });
32
- params.push(filter[key]);
33
- });
34
-
35
- excluded.forEach((key) => {
36
- filterClauses[key] = filterClauseMapping[key]({ exclude: true });
37
- });
38
- return { filterClauses, params };
39
- };
40
-
41
- const getNodesQuery = (filter: IFilter): [string, any[]] => {
42
- const { filterClauses, params } = getFilterClauses(filter);
43
- return [
44
- `
45
- SELECT
46
- c.jsonld_pivoted AS cycle,
47
- s.jsonld_pivoted['country'] AS region,
48
- COALESCE(
49
- jsonb_agg(ia.jsonld_pivoted ORDER BY ia.jsonld_pivoted->>'@id') FILTER (WHERE ia.jsonld_pivoted IS NOT NULL),
50
- '[]'
51
- ) AS "impactAssessments"
52
- FROM aggregated_cycles AS c
53
- LEFT JOIN aggregated_impact_assessments AS ia
54
- ON c.hestia_id = ia.cycle_hestia_id
55
- AND ${filterClauses.products.iaFilter}
56
- JOIN aggregated_sites AS s
57
- ON c.site_hestia_id = s.hestia_id
58
- AND ${filterClauses.regions}
59
- WHERE ${filterClauses.practices}
60
- AND ${filterClauses.defaultMethodClassifications}
61
- AND ${filterClauses.products.cycleFilter}
62
- AND ${filterClauses.minAggregatedQualityScore}
63
- AND ${filterClauses.periods}
64
- GROUP BY cycle, region
65
- ORDER BY c.jsonld_pivoted->>'@id'
66
- LIMIT 500
67
- `,
68
- params
69
- ];
70
- };
71
-
72
- export const applyFilter = async ({ filter }: { page?: number; filter: IFilter; query?: string }) => {
73
- // TODO: handle page and query later
74
- const [pgQuery, params] = getNodesQuery(filter);
75
- const result = await doQuery<AggregatedNodeRow>(pgQuery, params);
76
- return { count: result.rowCount, results: result.rows };
77
- };
package/src/app.spec.ts DELETED
@@ -1,34 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { logger } from './logger';
6
- import { app, healthcheck } from './app';
7
-
8
- let stubs: sinon.SinonStub[] = [];
9
-
10
- describe('app', () => {
11
- beforeEach(() => {
12
- stubs = [];
13
- stubs.push(sinon.stub(logger, 'error'));
14
- stubs.push(sinon.stub(logger, 'debug'));
15
- });
16
-
17
- afterEach(() => {
18
- stubs.forEach((stub) => stub.restore());
19
- });
20
-
21
- it('should be defined', () => {
22
- expect(app !== undefined).to.equal(true);
23
- });
24
-
25
- describe('healthcheck', () => {
26
- it('should return status 200', () => {
27
- const res = {
28
- status: sinon.stub().returns({ send() {} })
29
- };
30
- healthcheck(null, res);
31
- expect(res.status.calledWith(200)).to.equal(true);
32
- });
33
- });
34
- });
package/src/app.ts DELETED
@@ -1,59 +0,0 @@
1
- import * as express from 'express';
2
- import 'express-async-errors';
3
- const http2Express = require('http2-express-bridge');
4
- import { RequestHandler } from 'express';
5
-
6
- const app: express.Express = http2Express(express);
7
-
8
- import * as Sentry from '@sentry/node';
9
- import * as Tracing from '@sentry/tracing';
10
- import { sentryConfig } from './config';
11
-
12
- Sentry.init({
13
- ...sentryConfig,
14
- integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })]
15
- });
16
- app.use(Sentry.Handlers.requestHandler());
17
- app.use(Sentry.Handlers.tracingHandler());
18
-
19
- import cors from 'cors';
20
- import { disableCors } from './config';
21
- import { whitelistOrigin } from './cors';
22
- app.use(cors(disableCors ? undefined : { origin: whitelistOrigin }));
23
-
24
- import compression from 'compression';
25
- app.use(compression());
26
-
27
- import * as bodyParser from 'body-parser';
28
- // based on AWS Lambda body size limit
29
- const limit = '6mb';
30
- app.use(bodyParser.json({ limit }));
31
- app.use(bodyParser.urlencoded({ limit, extended: false }));
32
-
33
- export const healthcheck = (_req, res) => res.status(200).send();
34
- app.get('/health', healthcheck);
35
-
36
- import swagger from './swagger/routes';
37
- app.use('/swagger', swagger());
38
-
39
- // make sure settings is not blocked by maintenance mode
40
- import settings from './settings/routes';
41
- app.use('/settings', settings());
42
-
43
- import { maintenance } from './maintenance';
44
- // Until express v5 promises must be coerced to void
45
- // See https://github.com/davidbanham/express-async-errors/issues/36#issuecomment-944954003
46
- app.use(maintenance as RequestHandler);
47
-
48
- // log routes after healthcheck and maintenance
49
- import morgan from 'morgan';
50
- import { stream } from './logger';
51
- app.use(morgan('combined', { stream }));
52
-
53
- import routes from './routes';
54
- routes(app);
55
-
56
- import errorHandler from './errors';
57
- errorHandler(app);
58
-
59
- export { app };
package/src/config.ts DELETED
@@ -1,21 +0,0 @@
1
- const { version } = require('../package.json');
2
-
3
- export const PORT = process.env.PORT || '3000';
4
- export const apiUrl = (process.env.API_URL || 'http://localhost:{PORT}').replace('{PORT}', PORT);
5
- export const webappUrl = process.env.WEBAPP_URL || 'http://localhost';
6
-
7
- export const disableCors = (process.env.CORS_DISABLED || 'false') === 'true';
8
- export const whitelist = (process.env.CORS_WHITELIST || '').split(',');
9
- whitelist.push(apiUrl);
10
- whitelist.push(webappUrl);
11
-
12
- export const sentryConfig = {
13
- dsn: process.env.SENTRY_DSN,
14
- environment: process.env.SENTRY_ENV,
15
- release: version
16
- };
17
-
18
- export const slackConfig = {
19
- token: process.env.SLACK_TOKEN,
20
- channel: process.env.SLACK_CHANNEL
21
- };
package/src/cors.spec.ts DELETED
@@ -1,32 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { whitelistOrigin } from './cors';
6
-
7
- describe('cors', () => {
8
- describe('whitelistOrigin', () => {
9
- const origin = 'http://localhost';
10
- let callback: sinon.SinonStub;
11
-
12
- beforeEach(() => {
13
- callback = sinon.stub();
14
- });
15
-
16
- it('should allow localhost', () => {
17
- whitelistOrigin(origin, callback);
18
- expect(callback.calledWith(null, true)).to.equal(true);
19
- });
20
-
21
- it('should allow no origin', () => {
22
- whitelistOrigin(undefined, callback);
23
- expect(callback.calledWith(null, true)).to.equal(true);
24
- });
25
-
26
- it('should not allow other origin', () => {
27
- const other = 'other origin';
28
- whitelistOrigin(other, callback);
29
- expect(callback.calledWith(null, true)).to.equal(false);
30
- });
31
- });
32
- });
package/src/cors.ts DELETED
@@ -1,7 +0,0 @@
1
- import { whitelist } from './config';
2
- import { HestiaError } from './errors';
3
-
4
- export const whitelistOrigin = (origin: string, callback: Function) =>
5
- !origin || whitelist.includes(origin)
6
- ? callback(null, true)
7
- : callback(new HestiaError(`${origin} is not allowed by CORS`));
@@ -1,114 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { logger } from './logger';
6
- import { Errors, errorHandler, HestiaError } from './errors';
7
-
8
- class Response {
9
- statusCode = 200;
10
- json() {}
11
- }
12
-
13
- let stubs: sinon.SinonStub[] = [];
14
-
15
- describe('errors', () => {
16
- let errorStub: sinon.SinonStub;
17
-
18
- beforeEach(() => {
19
- stubs = [];
20
- stubs.push((errorStub = sinon.stub(logger, 'error')));
21
- });
22
-
23
- afterEach(() => {
24
- stubs.forEach((stub) => stub.restore());
25
- });
26
-
27
- describe('errorHandler', () => {
28
- let error: any = {
29
- message: 'error'
30
- };
31
- const response = new Response();
32
-
33
- it('should set status code to 500', () => {
34
- errorHandler(error, null, response as any);
35
- expect(response.statusCode).to.equal(500);
36
- });
37
-
38
- it('should render error as json', () => {
39
- let stub: sinon.SinonStub;
40
- stubs.push((stub = sinon.stub(response, 'json')));
41
- errorHandler(error, null, response as any);
42
-
43
- expect(
44
- stub.calledWith({
45
- message: error.message
46
- })
47
- ).to.equal(true);
48
- stub.restore();
49
- });
50
-
51
- it('should log Error stack', () => {
52
- const err = new Error();
53
- errorHandler(err, null, response as any);
54
- expect(errorStub.calledWith(err.stack)).to.equal(true);
55
- });
56
-
57
- describe('Hestia error', () => {
58
- beforeEach(() => {
59
- error = new HestiaError('error');
60
- });
61
-
62
- it('should not log the error', () => {
63
- errorHandler(error, null, response as any);
64
- expect(errorStub.called).to.equal(false);
65
- });
66
-
67
- it('should set status code to 400', () => {
68
- errorHandler(error, null, response as any);
69
- expect(response.statusCode).to.equal(400);
70
- });
71
- });
72
-
73
- describe(Errors.Unauthorized, () => {
74
- beforeEach(() => {
75
- error = { message: Errors.Unauthorized };
76
- });
77
-
78
- it('should set status code to 401', () => {
79
- errorHandler(error, null, response as any);
80
- expect(response.statusCode).to.equal(401);
81
- });
82
- });
83
-
84
- describe(Errors.NotFound, () => {
85
- beforeEach(() => {
86
- error = { message: Errors.NotFound };
87
- });
88
-
89
- it('should set status code to 404', () => {
90
- errorHandler(error, null, response as any);
91
- expect(response.statusCode).to.equal(404);
92
- });
93
- });
94
-
95
- describe('no message', () => {
96
- beforeEach(() => {
97
- error = {};
98
- });
99
-
100
- it('should render default error', () => {
101
- let stub: sinon.SinonStub;
102
- stubs.push((stub = sinon.stub(response, 'json')));
103
- errorHandler(error, null, response as any);
104
-
105
- expect(
106
- stub.calledWith({
107
- message: 'There was an error processing your request.'
108
- })
109
- ).to.equal(true);
110
- stub.restore();
111
- });
112
- });
113
- });
114
- });
package/src/errors.ts DELETED
@@ -1,121 +0,0 @@
1
- import { Express, Request, Response, NextFunction } from 'express';
2
- import * as Sentry from '@sentry/node';
3
-
4
- import { logger } from './logger';
5
-
6
- export enum Errors {
7
- UnderMaintenance = 'under-maintenance',
8
- ReadOnly = 'read-only',
9
- InvalidQueryParam = 'invalid-param-query',
10
- MissingQueryParam = 'missing-param-query',
11
- InvalidBodyParam = 'invalid-param-body',
12
- MissingBodyParam = 'missing-param-body',
13
- SchemaInvalid = 'invalid-schema',
14
- NotFound = 'not-found',
15
- Unauthorized = 'Unauthorized'
16
- }
17
-
18
- export enum StatusCodes {
19
- MethodNotAllowed = 405,
20
- RateLimit = 429,
21
- ServiceUnavailable = 503,
22
- InternalServerError = 500
23
- }
24
-
25
- export class HestiaError extends Error {
26
- constructor(message: string, public details = {}, public statusCode?: StatusCodes) {
27
- super(message);
28
- this.name = 'HestiaError';
29
- }
30
- }
31
-
32
- export const throwError = (message: string, details?: any, statusCode?: StatusCodes) => {
33
- throw new HestiaError(message, details, statusCode);
34
- };
35
-
36
- const isHestiaError = (error: Error) => error.name === 'HestiaError';
37
-
38
- export const shouldHandleError = (error: Error) => !isHestiaError(error);
39
-
40
- const statusCode = (error: Error) =>
41
- (error as any).statusCode ||
42
- (errorMessage(error).includes(Errors.NotFound)
43
- ? 404
44
- : error.message === Errors.Unauthorized
45
- ? 401
46
- : isHestiaError(error)
47
- ? 400
48
- : 500);
49
-
50
- export const errorMessage = (error?: Error) => error?.message || 'There was an error processing your request.';
51
-
52
- /**
53
- * @swagger
54
- *
55
- * components:
56
- * responses:
57
- * DefaultObject:
58
- * content:
59
- * application/json:
60
- * schema:
61
- * type: object
62
- * BadRequestError:
63
- * description: Bad Request
64
- * content:
65
- * application/json:
66
- * schema:
67
- * $ref: '#/components/schemas/Error'
68
- * UnauthorizedError:
69
- * description: Unauthorized
70
- * content:
71
- * application/json:
72
- * schema:
73
- * $ref: '#/components/schemas/Error'
74
- * example:
75
- * - message: Unauthorized
76
- * NotFoundError:
77
- * description: The requested ressource was not found
78
- * content:
79
- * application/json:
80
- * schema:
81
- * $ref: '#/components/schemas/Error'
82
- * UnexpectedError:
83
- * description: Unexpected error
84
- * content:
85
- * application/json:
86
- * schema:
87
- * $ref: '#/components/schemas/Error'
88
- * ServiceUnavailable:
89
- * description: Service Unavailable due to Maintenance
90
- * content:
91
- * application/json:
92
- * schema:
93
- * $ref: '#/components/schemas/Error'
94
- * NoContent:
95
- * description: Return nothing
96
- *
97
- * schemas:
98
- * Error:
99
- * type: object
100
- * required:
101
- * - message
102
- * properties:
103
- * message:
104
- * type: string
105
- */
106
- export const errorHandler = (err: any, _r: Request, res: Response, _n?: NextFunction) => {
107
- // error was not thrown intentionally
108
- const unexpectedError = shouldHandleError(err);
109
- unexpectedError && logger.error(err.stack ? err.stack : err);
110
- const message = errorMessage(err);
111
- res.statusCode = statusCode(err);
112
- return res.json({
113
- message,
114
- ...('details' in err ? { details: err.details } : {})
115
- });
116
- };
117
-
118
- export default (app: Express) => {
119
- app.use(Sentry.Handlers.errorHandler({ shouldHandleError }));
120
- app.use(errorHandler);
121
- };
package/src/index.spec.ts DELETED
@@ -1,94 +0,0 @@
1
- import { expect } from 'chai';
2
- import * as sinon from 'sinon';
3
- import 'mocha';
4
-
5
- import { run } from './index';
6
- import { app } from './app';
7
- import { logger } from './logger';
8
- import { PORT } from './config';
9
-
10
- let stubs: sinon.SinonStub[] = [];
11
-
12
- describe('run', () => {
13
- const server: any = {};
14
- let loggerStub: sinon.SinonStub;
15
-
16
- beforeEach(() => {
17
- stubs = [];
18
- });
19
-
20
- afterEach(() => {
21
- stubs.forEach((stub) => stub.restore());
22
- });
23
-
24
- describe('start error', () => {
25
- beforeEach(() => {
26
- loggerStub = sinon.stub(logger, 'error');
27
- stubs.push(loggerStub);
28
- });
29
-
30
- describe('error as string', () => {
31
- const error = 'error';
32
-
33
- beforeEach(() => {
34
- stubs.push(
35
- sinon.stub(app, 'listen').returns({
36
- on: (_p, callback: any) => {
37
- callback(error);
38
- return server;
39
- }
40
- } as any)
41
- );
42
- });
43
-
44
- it('should log the error', () => {
45
- run();
46
- expect(loggerStub.calledWith(error)).to.equal(true);
47
- });
48
- });
49
-
50
- describe('error as Error', () => {
51
- let error: Error;
52
-
53
- beforeEach(() => {
54
- error = new Error('error');
55
-
56
- stubs.push(
57
- sinon.stub(app, 'listen').returns({
58
- on: (_p, callback: any) => {
59
- callback(error);
60
- return server;
61
- }
62
- } as any)
63
- );
64
- });
65
-
66
- it('should log the error', () => {
67
- run();
68
- expect(loggerStub.calledWith(error.stack)).to.equal(true);
69
- });
70
- });
71
- });
72
-
73
- describe('start success', () => {
74
- beforeEach(() => {
75
- stubs.push(
76
- sinon.stub(app, 'listen').callsFake((_p, callback) => {
77
- callback();
78
- return {
79
- on: (_p1, _cb: any) => {
80
- return server;
81
- }
82
- } as any;
83
- })
84
- );
85
- loggerStub = sinon.stub(logger, 'info');
86
- stubs.push(loggerStub);
87
- });
88
-
89
- it('should log the port', () => {
90
- run();
91
- expect(loggerStub.calledWith(`App running on port ${PORT}`)).to.equal(true);
92
- });
93
- });
94
- });
package/src/index.ts DELETED
@@ -1,14 +0,0 @@
1
- import '../database';
2
- import { logger } from './logger';
3
- import { PORT } from './config';
4
- import { app } from './app';
5
-
6
- export const run = () => {
7
- const server = app
8
- .listen(PORT, () => logger.info(`App running on port ${PORT}`))
9
- .on('error', (err) => logger.error(err.stack || err));
10
-
11
- // ALB idle timeout set to 300 seconds
12
- server.keepAliveTimeout = 301 * 1000;
13
- server.headersTimeout = 305 * 1000;
14
- };
@@ -1,12 +0,0 @@
1
- import * as Sentry from '@sentry/serverless';
2
-
3
- import pkg from '../../package.json';
4
-
5
- Sentry.AWSLambda.init({
6
- dsn: process.env.SENTRY_DSN,
7
- environment: process.env.STAGE,
8
- release: pkg.version,
9
- enabled: process.env.IS_OFFLINE !== 'true'
10
- });
11
-
12
- export const wrapHandler = handler => Sentry.AWSLambda.wrapHandler(handler);