Skip to content

Commit

Permalink
Merge branch 'main' into feat/forms-1240-external-api
Browse files Browse the repository at this point in the history
  • Loading branch information
usingtechnology authored Jun 3, 2024
2 parents 754cbd4 + 44f5866 commit 4021a10
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG VARIANT="18.18.2-bullseye"
ARG VARIANT="18.20.3-bookworm"
FROM node:${VARIANT}

# not much in here, could acheive this another way for sure...
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dockerfile": "Dockerfile",
"context": "..",
"args": {
"VARIANT": "18.18.2-bullseye"
"VARIANT": "18.20.3-bookworm"
}
},

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM docker.io/node:18.18.2-alpine
FROM docker.io/node:18.20.3-alpine3.20

ENV NO_UPDATE_NOTIFIER=true
WORKDIR /opt/app-root/src/app
Expand Down
6 changes: 6 additions & 0 deletions app/src/docs/v1.api-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,12 @@ paths:
parameters:
- $ref: '#/components/parameters/formSubmissionIdParam'
- $ref: '#/components/parameters/documentTemplateIdParam'
- in: query
name: convertTo
schema:
type: string
description: file type to convert to
example: pdf
responses:
'200':
description: Returns the document template with merged variables
Expand Down
5 changes: 1 addition & 4 deletions app/src/forms/email/emailService.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,11 @@ const service = {
* @function submissionExportLink
* Email with the link to the Form submissions export file
* @param {string} formId
* @param {string} submissionId
* @param {object} body
* @param {string} referer
* @param {string} fileId
* @returns The result of the email merge operation
*/
submissionExportLink: async (formId, submissionId, body, referer, fileId) => {
submissionExportLink: async (formId, body, fileId) => {
try {
const form = await formService.readForm(formId);
const contextToVal = [body.to];
Expand Down Expand Up @@ -487,7 +485,6 @@ const service = {
function: EmailTypes.SUBMISSION_EXPORT,
formId: formId,
body: body,
referer: referer,
});
throw e;
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/forms/form/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ module.exports = {

export: async (req, res, next) => {
try {
const result = await exportService.export(req.params.formId, req.query, req.currentUser, req.headers.referer);
const result = await exportService.export(req.params.formId, req.query, req.currentUser);
['Content-Disposition', 'Content-Type'].forEach((h) => {
res.setHeader(h, result.headers[h.toLowerCase()]);
});
Expand All @@ -85,7 +85,7 @@ module.exports = {

exportWithFields: async (req, res, next) => {
try {
const result = await exportService.export(req.params.formId, req.body, req.currentUser, req.headers.referer);
const result = await exportService.export(req.params.formId, req.body, req.currentUser);
['Content-Disposition', 'Content-Type'].forEach((h) => {
res.setHeader(h, result.headers[h.toLowerCase()]);
});
Expand Down
42 changes: 24 additions & 18 deletions app/src/forms/form/exportService.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const service = {
}
return {};
},
_formatData: async (exportFormat, exportType, exportTemplate, form, data = {}, columns, version, emailExport, currentUser, referer) => {
_formatData: async (exportFormat, exportType, exportTemplate, form, data = {}, columns, version, emailExport, currentUser) => {
// inverting content structure nesting to prioritize submission content clarity
const formatted = data.map((obj) => {
const { submission, ...form } = obj;
Expand All @@ -181,7 +181,7 @@ const service = {
if (EXPORT_TYPES.submissions === exportType) {
if (EXPORT_FORMATS.csv === exportFormat) {
let formVersion = version ? parseInt(version) : 1;
return await service._formatSubmissionsCsv(form, formatted, exportTemplate, columns, formVersion, emailExport, currentUser, referer);
return await service._formatSubmissionsCsv(form, formatted, exportTemplate, columns, formVersion, emailExport, currentUser);
}
if (EXPORT_FORMATS.json === exportFormat) {
return await service._formatSubmissionsJson(form, formatted);
Expand Down Expand Up @@ -259,27 +259,33 @@ const service = {
},
};
},
_formatSubmissionsCsv: async (form, data, exportTemplate, fields, version, emailExport, currentUser, referer) => {
_formatSubmissionsCsv: async (form, data, exportTemplate, fields, version, emailExport, currentUser) => {
try {
switch (exportTemplate) {
case 'multiRowEmptySpacesCSVExport':
return service._multiRowsCSVExport(form, data, version, true, fields, emailExport, currentUser, referer);
return service._multiRowsCSVExport(form, data, version, true, fields, emailExport, currentUser);
case 'multiRowBackFilledCSVExport':
return service._multiRowsCSVExport(form, data, version, false, fields, emailExport, currentUser, referer);
return service._multiRowsCSVExport(form, data, version, false, fields, emailExport, currentUser);
case 'singleRowCSVExport':
return service._singleRowCSVExport(form, data, version, fields, currentUser, emailExport, referer);
return service._singleRowCSVExport(form, data, version, fields, currentUser, emailExport);
case 'unFormattedCSVExport':
return service._unFormattedCSVExport(form, data, emailExport, currentUser, referer);
return service._unFormattedCSVExport(form, data, emailExport, currentUser);
default:
// code block
throw new Problem(400, {
detail: `Bad export "template" value of "${exportTemplate}"`,
});
}
} catch (e) {
if (e instanceof Problem) {
throw e;
}

throw new Problem(500, {
detail: `Could not make a csv export of submissions for this form. ${e.message}`,
});
}
},
_multiRowsCSVExport: async (form, data, version, blankout, fields, emailExport, currentUser, referer) => {
_multiRowsCSVExport: async (form, data, version, blankout, fields, emailExport, currentUser) => {
const pathToUnwind = await unwindPath(data);
let headers = await service._buildCsvHeaders(form, data, version, fields);

Expand All @@ -288,22 +294,22 @@ const service = {
fields: headers,
};

return service._submissionCSVExport(opts, form, data, emailExport, currentUser, referer);
return service._submissionCSVExport(opts, form, data, emailExport, currentUser);
},
_singleRowCSVExport: async (form, data, version, fields, currentUser, emailExport, referer) => {
_singleRowCSVExport: async (form, data, version, fields, currentUser, emailExport) => {
const headers = await service._buildCsvHeaders(form, data, version, fields, true);
const opts = {
transforms: [flatten({ objects: true, arrays: true, separator: '.' })],
fields: headers,
};

return service._submissionCSVExport(opts, form, data, emailExport, currentUser, referer);
return service._submissionCSVExport(opts, form, data, emailExport, currentUser);
},
_unFormattedCSVExport: async (form, data, emailExport, currentUser, referer) => {
return service._submissionCSVExport({}, form, data, emailExport, currentUser, referer);
_unFormattedCSVExport: async (form, data, emailExport, currentUser) => {
return service._submissionCSVExport({}, form, data, emailExport, currentUser);
},

_submissionCSVExport(opts, form, data, emailExport, currentUser, referer) {
_submissionCSVExport(opts, form, data, emailExport, currentUser) {
// to work with object chunk in pipe instead of Buffer
const transformOpts = {
objectMode: true,
Expand Down Expand Up @@ -345,7 +351,7 @@ const service = {
// Uploading to Object storage
const fileResult = await fileService.create(fileData, fileCurrentUser, 'exports');
// Sending the email with link to uploaded export
emailService.submissionExportLink(form.id, null, { to: currentUser.email }, referer, fileResult.id);
emailService.submissionExportLink(form.id, { to: currentUser.email }, fileResult.id);
}
});
});
Expand Down Expand Up @@ -408,7 +414,7 @@ const service = {
return await service._buildCsvHeaders(form, formatted, params.version, undefined, params.singleRow === 'true');
},

export: async (formId, params = {}, currentUser = null, referer) => {
export: async (formId, params, currentUser) => {
// ok, let's determine what we are exporting and do it!!!!
// what operation?
// what output format?
Expand All @@ -417,7 +423,7 @@ const service = {
const exportTemplate = params.template ? params.template : 'multiRowEmptySpacesCSVExport';
const form = await service._getForm(formId);
const data = await service._getData(exportType, params.version, form, params);
const result = await service._formatData(exportFormat, exportType, exportTemplate, form, data, params.fields, params.version, params.emailExport, currentUser, referer);
const result = await service._formatData(exportFormat, exportType, exportTemplate, form, data, params.fields, params.version, params.emailExport, currentUser);
return { data: result.data, headers: result.headers };
},
};
Expand Down
5 changes: 3 additions & 2 deletions app/src/forms/submission/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ module.exports = {
const template = await formService.documentTemplateRead(req.params.documentTemplateId);
const fileName = template.filename.substring(0, template.filename.lastIndexOf('.'));
const fileExtension = template.filename.substring(template.filename.lastIndexOf('.') + 1);
const convertTo = req.query.convertTo || 'pdf';

const templateBody = {
data: {
Expand All @@ -148,12 +149,12 @@ module.exports = {
},
},
options: {
convertTo: 'pdf',
convertTo: convertTo,
overwrite: true,
reportName: fileName,
},
template: {
content: btoa(template.template),
content: template.template.toString(),
encodingType: 'base64',
fileType: fileExtension,
},
Expand Down
76 changes: 45 additions & 31 deletions app/tests/unit/forms/form/exportService.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,60 @@ const getCsvRow = (result, index) => {
};

describe('export', () => {
const form = {
id: formId,
snake: () => {
'form';
},
};
const formSchema = {
display: 'form',
type: 'form',
components: [
{
type: 'datagrid',
label: 'Data Grid',
components: [
{
type: 'simpletextfield',
label: 'Text Field',
},
],
},
],
};

// Mock the internal functions that only do Objection calls.
exportService._getForm = jest.fn().mockReturnValue(form);
exportService._readLatestFormSchema = jest.fn().mockReturnValue(formSchema);

describe('csv', () => {
const currentUser = {
usernameIdp: 'PAT_TEST',
};
const form = {
snake: () => {
'form';
},
};
exportService._getForm = jest.fn().mockReturnValue(form);

describe('400 response when', () => {
test('invalid preferences json', async () => {
const params = { preference: '{' };
exportService._readLatestFormSchema = jest.fn().mockReturnValueOnce();
const params = {
preference: '{',
};

await expect(exportService.export(formId, params, currentUser)).rejects.toThrow('400');
});

test('invalid export template', async () => {
exportService._getData = jest.fn().mockReturnValue([]);
const params = {
format: 'csv',
template: 'doesntexist',
type: 'submissions',
};

await expect(exportService.export(formId, params, currentUser)).rejects.toThrow('400');
});
});

describe('type 1', () => {
describe('type 1 / multiRowEmptySpacesCSVExport', () => {
const params = {
emailExport: false,
fields: ['form.submissionId', 'form.confirmationId', 'form.formName', 'form.version', 'form.createdAt', 'form.fullName', 'form.username', 'form.email', 'simpletextfield'],
Expand Down Expand Up @@ -73,30 +106,14 @@ describe('export', () => {
},
},
];
exportService._getData = jest.fn().mockReturnValueOnce(submission);
exportService._readLatestFormSchema = jest.fn().mockReturnValueOnce();
exportService._getData.mockReturnValueOnce(submission);
exportService._readLatestFormSchema.mockReturnValueOnce();

await expect(exportService.export(formId, params, currentUser)).rejects.toThrow('400');
});
});

describe('type 3', () => {
const latestFormSchema = {
display: 'form',
type: 'form',
components: [
{
type: 'datagrid',
label: 'Data Grid',
components: [
{
type: 'simpletextfield',
label: 'Text Field',
},
],
},
],
};
describe('type 3 / singleRowCSVExport', () => {
const params = {
emailExport: false,
fields: [
Expand Down Expand Up @@ -139,7 +156,6 @@ describe('export', () => {
},
];
exportService._getData = jest.fn().mockReturnValue(submission);
exportService._readLatestFormSchema = jest.fn().mockReturnValueOnce(latestFormSchema);

const result = await exportService.export(formId, params, currentUser);

Expand Down Expand Up @@ -173,7 +189,6 @@ describe('export', () => {
},
];
exportService._getData = jest.fn().mockReturnValue(submission);
exportService._readLatestFormSchema = jest.fn().mockReturnValueOnce(latestFormSchema);

const result = await exportService.export(formId, params, currentUser);

Expand Down Expand Up @@ -227,7 +242,6 @@ describe('export', () => {
},
];
exportService._getData = jest.fn().mockReturnValue(submission);
exportService._readLatestFormSchema = jest.fn().mockReturnValueOnce(latestFormSchema);

const result = await exportService.export(formId, params, currentUser);

Expand Down
2 changes: 1 addition & 1 deletion app/tests/unit/forms/submission/controller.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe('template rendering', () => {

const validDocumentTemplate = {
filename: 'template_hello_world.txt',
template: 'Hello {d.simpletextfield}!',
template: btoa('Hello {d.simpletextfield}!'),
};

const mockCdogsResponse = {
Expand Down
2 changes: 1 addition & 1 deletion openshift/app.cronjob.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ objects:
spec:
containers:
- name: my-container
image: docker.io/curlimages/curl:8.1.0
image: docker.io/curlimages/curl:8.8.0
env:
- name: APITOKEN
valueFrom:
Expand Down

0 comments on commit 4021a10

Please sign in to comment.