@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,10 +0,0 @@
1
- /* eslint-disable no-console */
2
- import * as dotenv from 'dotenv';
3
- dotenv.config();
4
-
5
- import { updateAggregatedNodes } from '../src/lambdas/update-aggregated-nodes/handler';
6
-
7
- updateAggregatedNodes().then(() => process.exit(0)).catch(err => {
8
- console.error(err);
9
- process.exit(1);
10
- });
@@ -1,18 +0,0 @@
1
- /* eslint-disable no-console */
2
- import * as dotenv from 'dotenv';
3
- dotenv.config();
4
-
5
- import { runMigrations } from '../database/migrations';
6
-
7
- void (async function () {
8
- const { gracefulExit } = await import('exit-hook');
9
-
10
- try {
11
- await runMigrations();
12
- gracefulExit(0);
13
- }
14
- catch (error) {
15
- console.error(error);
16
- gracefulExit(1);
17
- }
18
- })();
@@ -1,18 +0,0 @@
1
- /* eslint-disable no-console */
2
- import * as dotenv from 'dotenv';
3
- dotenv.config();
4
-
5
- import { resetDb } from '../test/utils';
6
-
7
- void (async function () {
8
- const { gracefulExit } = await import('exit-hook');
9
-
10
- try {
11
- await resetDb();
12
- gracefulExit(0);
13
- }
14
- catch (error) {
15
- console.error(error);
16
- gracefulExit(1);
17
- }
18
- })();
@@ -1,18 +0,0 @@
1
- /* eslint-disable no-console */
2
- import * as dotenv from 'dotenv';
3
- dotenv.config();
4
-
5
- import { runSeed } from '../database/seed';
6
-
7
- void (async function () {
8
- const { gracefulExit } = await import('exit-hook');
9
-
10
- try {
11
- await runSeed();
12
- gracefulExit(0);
13
- }
14
- catch (error) {
15
- console.error(error);
16
- gracefulExit(1);
17
- }
18
- })();
package/serverless.yml DELETED
@@ -1,101 +0,0 @@
1
- frameworkVersion: '3'
2
-
3
- service: hestia-data-api
4
-
5
- plugins:
6
- - serverless-deployment-bucket
7
- - serverless-offline
8
-
9
- package:
10
- exclude:
11
- - node_modules/@types/**
12
- # cannot be removed as used in migrations
13
- # - node_modules/typescript/**
14
- - node_modules/**/*.map
15
- - node_modules/**/*.d.ts
16
- - node_modules/**/coverage/**
17
- - node_modules/**/tests/**
18
- - node_modules/**/test/**
19
- - .nyc_output/**
20
- - coverage/**
21
- - data/**
22
- - docs/**
23
- - envs/**
24
- - samples/**
25
- - scripts/**
26
- - test/**
27
- - tmp/**
28
- - tsconfig*
29
- - "*.yml"
30
- - "*.md"
31
- - "*.sh"
32
- - ".dockerignore"
33
- - "Dockerfile"
34
- - "gulpfile.js"
35
-
36
- custom:
37
- serverless-offline:
38
- httpPort: 3002
39
- stage: ${env:STAGE, self:provider.stage}
40
- securityGroup:
41
- staging: sg-0800627978a40527b
42
- prod: sg-049204c3b58a8bd08
43
- subnetId1:
44
- # private 1a
45
- staging: subnet-04ad6e355249c757d
46
- prod: subnet-00c4a891bce6f48e0
47
- subnetId2:
48
- # private 1b
49
- staging: subnet-01260544aaa3c6810
50
- prod: subnet-02365f46c569f5e1a
51
- debugging:
52
- staging: true
53
- prod: false
54
- logLevel:
55
- staging: DEBUG
56
- prod: INFO
57
- bucket: hestia-data-${self:custom.stage}
58
-
59
- provider:
60
- name: aws
61
- runtime: nodejs18.x
62
- region: us-east-1
63
- stage: staging
64
- deploymentBucket:
65
- name: hestia-serverless-${self:custom.stage}
66
- memorySize: 512
67
- timeout: 600
68
- environment:
69
- SERVICE_NAME: ${self:service}
70
- STAGE: ${self:custom.stage}
71
- DEBUG: ${self:custom.debugging.${self:custom.stage}}
72
- LOG_LEVEL: ${self:custom.logLevel.${self:custom.stage}}
73
- SENTRY_DSN: https://00ed710e2b664894a3ff3124bfff24cf@o441427.ingest.sentry.io/4505186792767488
74
- BUCKET: ${self:custom.bucket}
75
- PGHOST: ${env:PGHOST}
76
- PGDATABASE: ${env:PGDATABASE}
77
- PGPORT: ${env:PGPORT}
78
- PGUSER: ${env:PGUSER}
79
- PGPASSWORD: ${env:PGPASSWORD}
80
- iamRoleStatements:
81
- - Effect: Allow
82
- Action:
83
- - s3:GetObject
84
- - s3:GetObjectAcl
85
- - s3:ListObjects
86
- - s3:ListBucket
87
- Resource:
88
- - "arn:aws:s3:::${self:custom.bucket}"
89
- - "arn:aws:s3:::${self:custom.bucket}/*"
90
- vpc:
91
- securityGroupIds:
92
- - ${self:custom.securityGroup.${self:custom.stage}}
93
- subnetIds:
94
- - ${self:custom.subnetId1.${self:custom.stage}}
95
- - ${self:custom.subnetId2.${self:custom.stage}}
96
-
97
- functions:
98
- updateAggregatedNodes:
99
- handler: build/src/lambdas/update-aggregated-nodes/handler.updateAggregatedNodes
100
- events:
101
- - schedule: cron(0 2 * * ? *) # 2:00am every day
@@ -1,37 +0,0 @@
1
- import { ISiteJSONLD } from '@hestia-earth/schema';
2
-
3
- export interface IFilter {
4
- regions?: string[];
5
- periods?: string[];
6
- products?: string[];
7
- defaultMethodClassifications?: string[];
8
- practices?: string[];
9
- minAggregatedQualityScore?: number;
10
- }
11
-
12
- export const validFilterFields = [
13
- 'regions',
14
- 'periods',
15
- 'products',
16
- 'defaultMethodClassifications',
17
- 'practices',
18
- 'minAggregatedQualityScore'
19
- ];
20
-
21
- export interface IFilters {
22
- cycleCount: number;
23
- impactAssessmentCount: number;
24
- products: string[];
25
- periods: string[];
26
- practices: string[];
27
- minAggregatedQualityScore: number[];
28
- regions: string[];
29
- defaultMethodClassifications: string[];
30
- }
31
-
32
- // TODO: update with pivoted types when available
33
- export interface AggregatedNodeRow {
34
- cycle: any; // pivoted cycle
35
- region: ISiteJSONLD['country'];
36
- impactAssessments: [any]; // pivoted impact assessments
37
- }
@@ -1,54 +0,0 @@
1
- import { Request } from 'express';
2
-
3
- import { applyFilters } from '../services/pg-get-filters';
4
-
5
- /**
6
- * @swagger
7
- *
8
- * paths:
9
- * /aggregated-nodes/pg/filters:
10
- * get:
11
- * summary: "Get the additional filters available for a filter selection and node counts (uses PG only)"
12
- * parameters:
13
- * - in: query
14
- * name: filter
15
- * schema:
16
- * type: string
17
- * examples:
18
- * example1:
19
- * summary: Barley in Australia and India between 2010 and 2019.
20
- * value: "regions=GADM-AUS|GADM-IND;\
21
- * products=barleyGrainWhole|barleyStraw;\
22
- * periods=2010-2019"
23
- * example2:
24
- * summary: Wheat and Maize in France, United Kingdom and Worldwide between 2000 and 2019.
25
- * value: "regions=region-world|GADM-GBR|GADM-FRA;\
26
- * products=wheatGrain|maizeGrain;\
27
- * periods=2000-2009|2010-2019"
28
- * description: Set of filters
29
- * - in: query
30
- * name: query
31
- * schema:
32
- * type: string
33
- * example: Wheat
34
- * description: Free search text
35
- * responses:
36
- * 200:
37
- * content:
38
- * application/json:
39
- * schema:
40
- * type: object
41
- * 400:
42
- * $ref: '#/components/responses/BadRequestError'
43
- * 404:
44
- * $ref: '#/components/responses/NotFoundError'
45
- * 500:
46
- * $ref: '#/components/responses/UnexpectedError'
47
- * 503:
48
- * $ref: '#/components/responses/ServiceUnavailable'
49
- */
50
-
51
- export const getFilters = ({ query: qs, filter }: Request) => {
52
- const query = qs.query as string;
53
- return applyFilters({ filter, query });
54
- };
@@ -1,61 +0,0 @@
1
- /* eslint-disable max-len */
2
- import { Request } from 'express';
3
-
4
- import { applyFilter } from '../services/pg-get';
5
-
6
- /**
7
- * @swagger
8
- *
9
- * paths:
10
- * /aggregated-nodes/pg:
11
- * get:
12
- * summary: "Get the aggregated nodes corresponding to a set of filters and search query (uses PG only)"
13
- * parameters:
14
- * - in: query
15
- * name: filter
16
- * schema:
17
- * type: string
18
- * examples:
19
- * example1:
20
- * summary: Barley in Australia and India between 2010 and 2019.
21
- * value: "regions=GADM-AUS|GADM-IND;\
22
- * products=barleyGrainWhole|barleyStraw;\
23
- * periods=2010-2019"
24
- * example2:
25
- * summary: Wheat and Maize in France, United Kingdom and Worldwide between 2000 and 2019.
26
- * value: "regions=region-world|GADM-GBR|GADM-FRA;\
27
- * products=wheatGrain|maizeGrain;\
28
- * periods=2000-2009|2010-2019"
29
- * description: Set of filters
30
- * - in: query
31
- * name: page
32
- * schema:
33
- * type: integer
34
- * example: 1
35
- * - in: query
36
- * name: query
37
- * schema:
38
- * type: string
39
- * example: Wheat
40
- * description: Free search text
41
- * responses:
42
- * 200:
43
- * content:
44
- * application/json:
45
- * schema:
46
- * type: object
47
- * 400:
48
- * $ref: '#/components/responses/BadRequestError'
49
- * 404:
50
- * $ref: '#/components/responses/NotFoundError'
51
- * 500:
52
- * $ref: '#/components/responses/UnexpectedError'
53
- * 503:
54
- * $ref: '#/components/responses/ServiceUnavailable'
55
- */
56
-
57
- export const get = ({ query: qs, filter }: Request) => {
58
- const page = Number(qs.page) || 1;
59
- const query = qs.query as string;
60
- return applyFilter({ page, filter, query });
61
- };
@@ -1,274 +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 { Errors, errorHandler } from '../errors';
10
- import { initDb } from '../../test/utils';
11
- import { seed } from '../../database/seed';
12
-
13
- import * as fixtures from '../../test/fixtures/aggregated-nodes/get';
14
-
15
- let stubs: sinon.SinonStub[] = [];
16
-
17
- const app = express();
18
-
19
- const getFixtureNode = (type: 'site' | 'cycle' | 'impactAssessment', id: string) =>
20
- fixtures[
21
- { cycle: 'aggregatedCycles', site: 'aggregatedSites', impactAssessment: 'aggregatedImpactAssessments' }[type]
22
- ].find((node) => node.jsonld_pivoted['@id'] === id).jsonld_pivoted;
23
-
24
- app.use(bodyParser.json());
25
- app.use(router());
26
- app.use(errorHandler);
27
-
28
- describe('routes', () => {
29
- beforeEach(() => {
30
- stubs = [];
31
- });
32
-
33
- afterEach(() => {
34
- stubs.forEach((stub) => stub.restore());
35
- });
36
-
37
- describe('aggregated-nodes', () => {
38
- beforeEach(async () => {
39
- await initDb();
40
- await seed(fixtures);
41
- });
42
-
43
- describe('GET /pg', () => {
44
- it('when invalid filter passed returns invalid param error', async () => {
45
- const result = await supertest(app)
46
- .get('/pg')
47
- .query({ filter: 'invalidfilter' })
48
- .set('Accept', 'application/json');
49
-
50
- expect(result.body.message).to.equal(Errors.InvalidQueryParam);
51
- });
52
-
53
- it('when neither filter nor query passed returns missing param error', async () => {
54
- const result = await supertest(app).get('/pg').query({}).set('Accept', 'application/json');
55
-
56
- expect(result.body.message).to.equal(Errors.MissingQueryParam);
57
- });
58
-
59
- it('can filter by regions', async () => {
60
- const expected = [
61
- {
62
- cycle: getFixtureNode('cycle', '1'),
63
- region: getFixtureNode('site', '1').country,
64
- impactAssessments: [getFixtureNode('impactAssessment', '1'), getFixtureNode('impactAssessment', '2')]
65
- },
66
- {
67
- cycle: getFixtureNode('cycle', '2'),
68
- region: getFixtureNode('site', '1').country,
69
- impactAssessments: [getFixtureNode('impactAssessment', '3')]
70
- },
71
- {
72
- cycle: getFixtureNode('cycle', '3'),
73
- region: getFixtureNode('site', '2').country,
74
- impactAssessments: [getFixtureNode('impactAssessment', '4')]
75
- }
76
- ];
77
-
78
- const filter = 'regions=GADM-POL|GADM-DEU';
79
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
80
- expect(result.body.results).to.deep.equal(expected);
81
- });
82
-
83
- it('can filter by practices', async () => {
84
- const expected = [
85
- {
86
- cycle: getFixtureNode('cycle', '1'),
87
- region: getFixtureNode('site', '1').country,
88
- impactAssessments: [getFixtureNode('impactAssessment', '1'), getFixtureNode('impactAssessment', '2')]
89
- },
90
- {
91
- cycle: getFixtureNode('cycle', '2'),
92
- region: getFixtureNode('site', '1').country,
93
- impactAssessments: [getFixtureNode('impactAssessment', '3')]
94
- }
95
- ];
96
- const filter = 'practices=earthingUpByHand|deepRipping';
97
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
98
- expect(result.body.results).to.deep.equal(expected);
99
- });
100
-
101
- it('can filter by minimum aggregatedQualityScore', async () => {
102
- const expected = [
103
- {
104
- cycle: getFixtureNode('cycle', '1'),
105
- region: getFixtureNode('site', '1').country,
106
- impactAssessments: [getFixtureNode('impactAssessment', '1'), getFixtureNode('impactAssessment', '2')]
107
- }
108
- ];
109
- const filter = 'minAggregatedQualityScore=3';
110
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
111
- expect(result.body.results).to.deep.equal(expected);
112
- });
113
-
114
- it('can filter by defaultMethodClassifications', async () => {
115
- const expected = [
116
- {
117
- cycle: getFixtureNode('cycle', '2'),
118
- region: getFixtureNode('site', '1').country,
119
- impactAssessments: [getFixtureNode('impactAssessment', '3')]
120
- },
121
- {
122
- cycle: getFixtureNode('cycle', '3'),
123
- region: getFixtureNode('site', '2').country,
124
- impactAssessments: [getFixtureNode('impactAssessment', '4')]
125
- }
126
- ];
127
- const filter = 'defaultMethodClassifications=physical measurement|expert opinion';
128
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
129
- expect(result.body.results).to.deep.equal(expected);
130
- });
131
-
132
- it('can filter by periods', async () => {
133
- const expected = [
134
- {
135
- cycle: getFixtureNode('cycle', '3'),
136
- region: getFixtureNode('site', '2').country,
137
- impactAssessments: [getFixtureNode('impactAssessment', '4')]
138
- },
139
- {
140
- cycle: getFixtureNode('cycle', '4'),
141
- region: getFixtureNode('site', '3').country,
142
- impactAssessments: []
143
- }
144
- ];
145
- const filter = 'periods=2010-2019|2000-2009';
146
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
147
- expect(result.body.results).to.deep.equal(expected);
148
- });
149
-
150
- it('can filter by products', async () => {
151
- const expected = [
152
- {
153
- cycle: getFixtureNode('cycle', '1'),
154
- region: getFixtureNode('site', '1').country,
155
- // impactAssessments have also been filtered
156
- impactAssessments: [getFixtureNode('impactAssessment', '1')]
157
- },
158
- {
159
- cycle: getFixtureNode('cycle', '2'),
160
- region: getFixtureNode('site', '1').country,
161
- impactAssessments: [getFixtureNode('impactAssessment', '3')]
162
- }
163
- ];
164
- const filter = 'products=wheatGrain|fishFingerlings';
165
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
166
- expect(result.body.results).to.deep.equal(expected);
167
- });
168
-
169
- it('can combine multiple filters', async () => {
170
- const expected = [
171
- {
172
- cycle: getFixtureNode('cycle', '5'),
173
- region: getFixtureNode('site', '3').country,
174
- // impactAssessments have also been filtered
175
- impactAssessments: []
176
- }
177
- ];
178
- const filter = [
179
- 'regions=GADM-POL|GADM-DEU|GADM-BRA',
180
- 'products=wheatGrain|fishFingerlings|cheese',
181
- 'periods=2010-2019|2020-2029',
182
- 'defaultMethodClassifications=modelled|expert opinion',
183
- 'minAggregatedQualityScore=2',
184
- 'practices=earthingUpByHand|deepRipping|baggingFruit'
185
- ].join(';');
186
- const result = await supertest(app).get('/pg').query({ filter }).set('Accept', 'application/json');
187
- expect(result.body.results).to.deep.equal(expected);
188
- });
189
- });
190
-
191
- describe('GET /pg/filters', () => {
192
- it('when invalid filter passed returns invalid param error', async () => {
193
- const result = await supertest(app)
194
- .get('/pg/filters')
195
- .query({ filter: 'invalidfilter' })
196
- .set('Accept', 'application/json');
197
-
198
- expect(result.body.message).to.equal(Errors.InvalidQueryParam);
199
- });
200
-
201
- describe('returns the filters and node counts available for the current selection', () => {
202
- it('returns subset of filters corresponding to selected nodes', async () => {
203
- const expected = {
204
- cycleCount: 1,
205
- impactAssessmentCount: 0,
206
- products: ['apples', 'cheese'],
207
- periods: ['2020-2029'],
208
- practices: ['baggingFruit'],
209
- minAggregatedQualityScore: [2],
210
- regions: ['GADM-BRA'],
211
- defaultMethodClassifications: ['modelled']
212
- };
213
- const filter = [
214
- 'regions=GADM-POL|GADM-DEU|GADM-BRA',
215
- 'products=wheatGrain|fishFingerlings|cheese',
216
- 'periods=2010-2019|2020-2029',
217
- 'defaultMethodClassifications=modelled|expert opinion',
218
- 'minAggregatedQualityScore=2',
219
- 'practices=earthingUpByHand|deepRipping|baggingFruit'
220
- ].join(';');
221
- const result = await supertest(app).get('/pg/filters').query({ filter }).set('Accept', 'application/json');
222
- expect(result.body).to.deep.equal(expected);
223
- });
224
-
225
- it('returns all filters when there is nothing currently selected', async () => {
226
- const expected = {
227
- cycleCount: 9,
228
- impactAssessmentCount: 4,
229
- products: ['apples', 'barley', 'cheese', 'fishFingerlings', 'wheatGrain'],
230
- periods: ['1980-1989', '1990-1999', '2000-2009', '2010-2019', '2020-2029'],
231
- practices: ['baggingFruit', 'deepRipping', 'earthingUpByHand'],
232
- minAggregatedQualityScore: [1, 2, 3],
233
- regions: ['GADM-BRA', 'GADM-DEU', 'GADM-POL'],
234
- defaultMethodClassifications: ['expert opinion', 'modelled', 'physical measurement', 'unsourced assumption']
235
- };
236
- const result = await supertest(app).get('/pg/filters').query({}).set('Accept', 'application/json');
237
- expect(result.body).to.deep.equal(expected);
238
- });
239
-
240
- it('does not exclude nodes with missing practices key', async () => {
241
- const expected = {
242
- cycleCount: 1,
243
- impactAssessmentCount: 0,
244
- products: ['apples', 'cheese'],
245
- periods: ['1980-1989'],
246
- practices: [],
247
- minAggregatedQualityScore: [2],
248
- regions: ['GADM-BRA'],
249
- defaultMethodClassifications: ['modelled']
250
- };
251
- const filter = ['regions=GADM-BRA', 'periods=1980-1989'].join(';');
252
- const result = await supertest(app).get('/pg/filters').query({ filter }).set('Accept', 'application/json');
253
- expect(result.body).to.deep.equal(expected);
254
- });
255
-
256
- it('returns empty arrays when no filters available', async () => {
257
- const expected = {
258
- cycleCount: 0,
259
- impactAssessmentCount: 0,
260
- products: [],
261
- periods: [],
262
- practices: [],
263
- minAggregatedQualityScore: [],
264
- regions: [],
265
- defaultMethodClassifications: []
266
- };
267
- const filter = ['products=grapes'].join(';');
268
- const result = await supertest(app).get('/pg/filters').query({ filter }).set('Accept', 'application/json');
269
- expect(result.body).to.deep.equal(expected);
270
- });
271
- });
272
- });
273
- });
274
- });
@@ -1,56 +0,0 @@
1
- import { Request, Response, NextFunction, Router } from 'express';
2
- import { endpointWrapper } from '../utils/endpoint-wrapper';
3
- import { get as pgGet } from './routes/pg-get';
4
- import { HestiaError, Errors } from '../errors';
5
- import { validFilterFields } from './model';
6
- import { getFilters as pgGetFilters } from './routes/pg-get-filters';
7
- import { IFilter } from './model';
8
-
9
- const isValidFilterString = (filter: string) =>
10
- filter.split(';').every((field) => {
11
- const [key] = field.split('=');
12
- return validFilterFields.includes(key);
13
- });
14
-
15
- const validateGet = (req: Request, _res: Response, next: NextFunction) => {
16
- const missingParam = !req.query.filter && !req.query.query;
17
- const invalidParam = req.query.filter && !isValidFilterString(req.query.filter as string);
18
- return missingParam
19
- ? next(new HestiaError(Errors.MissingQueryParam))
20
- : invalidParam
21
- ? next(new HestiaError(Errors.InvalidQueryParam))
22
- : next();
23
- };
24
-
25
- const validateGetFilters = (req: Request, _res: Response, next: NextFunction) => {
26
- const invalidParam = req.query.filter && !isValidFilterString(req.query.filter as string);
27
- return invalidParam ? next(new HestiaError(Errors.InvalidQueryParam)) : next();
28
- };
29
-
30
- const parseArrayField = (val: string) => val.split('|');
31
-
32
- const filterFieldParsers: Record<keyof IFilter, (val: string) => any> = {
33
- regions: parseArrayField,
34
- periods: parseArrayField,
35
- products: parseArrayField,
36
- defaultMethodClassifications: parseArrayField,
37
- minAggregatedQualityScore: (val: string) => Number(val),
38
- practices: parseArrayField
39
- };
40
-
41
- const parseFilterString = (req: Request, _res: Response, next: NextFunction) => {
42
- req.filter = ((req.query.filter || '') as string).split(';').reduce((acc, entry) => {
43
- const [key, val] = entry.split('=');
44
- return filterFieldParsers[key] ? { ...acc, [key]: filterFieldParsers[key](val) } : acc;
45
- }, {});
46
- next();
47
- };
48
-
49
- export default () => {
50
- const router = Router();
51
-
52
- router.get('/pg/filters', validateGetFilters, parseFilterString, endpointWrapper(pgGetFilters));
53
- router.get('/pg', validateGet, parseFilterString, endpointWrapper(pgGet));
54
-
55
- return router;
56
- };