Skip to content

Commit 7ba497f

Browse files
Merge pull request #1195 from ReliefApplications/beta
Beta
2 parents 59783da + b2028e4 commit 7ba497f

File tree

7 files changed

+133
-16
lines changed

7 files changed

+133
-16
lines changed

__tests__/unit-tests/routes/activity/activity.service.spec.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,25 @@ describe('Activity Service', () => {
216216
const query = {};
217217
const aggregationResult = [];
218218
const listAggregationMock = jest.spyOn(service as any, 'listAggregation');
219-
listAggregationMock.mockResolvedValueOnce(aggregationResult);
219+
// Count
220+
listAggregationMock.mockReturnValueOnce({
221+
count: () => [{ count: 20000 }],
222+
});
223+
// 2 pages
224+
listAggregationMock.mockReturnValueOnce({
225+
skip: jest.fn().mockReturnThis(),
226+
limit: jest.fn().mockResolvedValueOnce(aggregationResult),
227+
});
228+
listAggregationMock.mockReturnValueOnce({
229+
skip: jest.fn().mockReturnThis(),
230+
limit: jest.fn().mockResolvedValueOnce(aggregationResult),
231+
});
220232

221233
const mockFile = Buffer.from('Test content');
222234
jest.spyOn(XLSBuilder, 'default').mockResolvedValueOnce(mockFile as any);
223235

224236
const result = await service.downloadList(query);
225-
expect(listAggregationMock).toHaveBeenCalled();
237+
expect(listAggregationMock).toHaveBeenCalledTimes(3); // 1 for count, 2 for pagination
226238
expect(result).toEqual({
227239
fileName: (service as any).listExportFileName,
228240
file: mockFile,
@@ -235,7 +247,11 @@ describe('Activity Service', () => {
235247

236248
const listAggregationMock = jest
237249
.spyOn(service as any, 'listAggregation')
238-
.mockRejectedValueOnce(error);
250+
.mockReturnValueOnce({
251+
count: () => {
252+
throw error;
253+
},
254+
});
239255

240256
await expect(service.downloadList(query)).rejects.toThrow(error);
241257
expect(listAggregationMock).toHaveBeenCalled();

package-lock.json

+39
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@ucast/mongo2js": "^1.3.3",
4646
"apollo-server-cache-redis": "^3.3.1",
4747
"axios": "^1.4.0",
48+
"axios-cache-interceptor": "^1.6.2",
4849
"body-parser": "^1.20.1",
4950
"bson": "^4.2.3",
5051
"config": "^3.3.9",

src/routes/activity/activity.service.ts

+24-9
Original file line numberDiff line numberDiff line change
@@ -352,18 +352,33 @@ export class ActivityService {
352352
filters.push(queryFilters);
353353
}
354354

355-
const aggregation = await this.listAggregation(
355+
const totalCountAggregation = await this.listAggregation(
356356
sortField,
357357
sortOrder,
358358
filters
359-
);
360-
const formattedData = aggregation.map((activity) => ({
361-
timestamp: this.formatDate(activity.createdAt, timeZone),
362-
userId: activity.userId?.toString(),
363-
page: activity.metadata?.title || '',
364-
username: activity.username,
365-
attributes: activity.attributes,
366-
}));
359+
).count('count');
360+
361+
const formattedData = [];
362+
363+
for (let skip = 0; skip < totalCountAggregation[0].count; skip += 10000) {
364+
const aggregation = await this.listAggregation(
365+
sortField,
366+
sortOrder,
367+
filters
368+
)
369+
.skip(skip)
370+
.limit(1000);
371+
formattedData.push(
372+
...aggregation.map((activity) => ({
373+
timestamp: this.formatDate(activity.createdAt, timeZone),
374+
userId: activity.userId?.toString(),
375+
page: activity.metadata?.title || '',
376+
username: activity.username,
377+
attributes: activity.attributes,
378+
}))
379+
);
380+
}
381+
367382
const file = await xlsBuilder(
368383
this.listExportFileName,
369384
this.listExportColumns,

src/server/common-services.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Axios from 'axios';
2+
import {
3+
AxiosCacheInstance,
4+
setupCache,
5+
buildKeyGenerator,
6+
} from 'axios-cache-interceptor/dev';
7+
8+
let axios: AxiosCacheInstance;
9+
10+
/**
11+
* Create a dedicated axios instance for Common Services
12+
* Add cache mechanism
13+
* Cached requests expire after 5 minutes.
14+
*
15+
* @returns Common Services axios instance
16+
*/
17+
export default () => {
18+
if (!axios) {
19+
const instance = Axios.create();
20+
axios = setupCache(instance, {
21+
methods: ['get', 'post'],
22+
// Avoid using cache control set to false
23+
interpretHeader: false,
24+
debug: console.log,
25+
// Generate an unique key, based on all parameters listed
26+
generateKey: buildKeyGenerator((request) => ({
27+
method: request.method,
28+
baseURL: request.baseURL,
29+
params: request.params,
30+
url: request.url,
31+
data: request.data,
32+
custom: request.headers.Authorization,
33+
})),
34+
});
35+
}
36+
return axios;
37+
};

src/utils/form/getDisplayText.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { Context } from '../../server/apollo/context';
22
import { CustomAPI } from '../../server/apollo/dataSources';
33
import config from 'config';
44
import { logger } from '@services/logger.service';
5-
import axios, { AxiosHeaders } from 'axios';
5+
import axios, { AxiosHeaders, AxiosStatic } from 'axios';
66
import get from 'lodash/get';
77
import jsonpath from 'jsonpath';
8+
import commonServices from '@server/common-services';
9+
import { AxiosCacheInstance } from 'axios-cache-interceptor';
810

911
/**
1012
* Gets display text from choice value.
@@ -114,6 +116,7 @@ export const getFullChoices = async (
114116
}
115117
}
116118
} else if (field.choicesByGraphQL) {
119+
let sender: AxiosCacheInstance | AxiosStatic = axios;
117120
const url: string = field.choicesByGraphQL.url;
118121
let choices: any[] = [];
119122
const valueField = get(field, 'choicesByGraphQL.value', null);
@@ -126,13 +129,14 @@ export const getFullChoices = async (
126129
url.includes(config.get('commonServices.url'))
127130
) {
128131
headers.setAuthorization(`Bearer ${context.accesstoken}`);
132+
sender = commonServices();
129133
} else {
130134
headers.setAuthorization(context.token);
131135
if (context.accesstoken) {
132136
headers.set('accesstoken', context.accesstoken);
133137
}
134138
}
135-
await axios({
139+
await sender({
136140
url,
137141
method: 'post',
138142
headers,

src/utils/proxy/getChoices.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import fetch from 'node-fetch';
22
import get from 'lodash/get';
33
import jsonpath from 'jsonpath';
4-
import axios, { AxiosHeaders } from 'axios';
4+
import { AxiosHeaders, AxiosStatic } from 'axios';
55
import config from 'config';
66
import { Request } from 'express';
7+
import commonServices from '@server/common-services';
8+
import { AxiosCacheInstance } from 'axios-cache-interceptor';
9+
import axios from 'axios';
710

811
/**
912
* Fetches the choices for a question field by URL
@@ -63,11 +66,13 @@ export const getChoices = async (req: Request, field: any): Promise<any[]> => {
6366
const url = get(field, 'choicesByGraphQL.url', null);
6467
const valueField = get(field, 'choicesByGraphQL.value', null);
6568
const textField = get(field, 'choicesByGraphQL.text', null);
69+
let sender: AxiosCacheInstance | AxiosStatic = axios;
6670
if (
6771
config.get('commonServices.url') &&
6872
url.includes(config.get('commonServices.url'))
6973
) {
7074
headers.setAuthorization(`Bearer ${req.headers.accesstoken}`);
75+
sender = commonServices();
7176
} else {
7277
headers.setAuthorization(req.headers.authorization);
7378
if (req.headers.accesstoken) {
@@ -76,7 +81,7 @@ export const getChoices = async (req: Request, field: any): Promise<any[]> => {
7681
}
7782
try {
7883
let choices: any[] = [];
79-
await axios({
84+
await sender({
8085
url,
8186
method: 'post',
8287
headers,

0 commit comments

Comments
 (0)