11import readline from "node:readline" ;
2- import filesystem from "node:fs/promises" ;
32import type {
3+ Attachment ,
44 FormSubmission ,
55 FormSubmissionProblem ,
66 PrivateApiKey ,
@@ -10,6 +10,10 @@ import { GCFormsApiClient } from "./gcFormsApiClient.js";
1010import { decryptFormSubmission } from "./formSubmissionDecrypter.js" ;
1111import { verifyIntegrity } from "./formSubmissionIntegrityVerifier.js" ;
1212import path from "node:path" ;
13+ import { createWriteStream } from "node:fs" ;
14+ import { mkdir , readdir , readFile , writeFile } from "node:fs/promises" ;
15+ import axios from "axios" ;
16+ import type { Readable } from "node:stream" ;
1317
1418const IDENTITY_PROVIDER_URL = "https://auth.forms-formulaires.alpha.canada.ca" ;
1519const PROJECT_IDENTIFIER = "284778202772022819" ;
@@ -106,7 +110,9 @@ async function runRetrieveDecryptAndConfirmFormSubmissions(
106110 const endGetFormSubmissionTimer = performance . now ( ) ;
107111
108112 console . info (
109- `\nForm submission retrieved in ${ Math . round ( endGetFormSubmissionTimer - startGetFormSubmissionTimer ) } ms` ,
113+ `\nForm submission retrieved in ${ Math . round (
114+ endGetFormSubmissionTimer - startGetFormSubmissionTimer ,
115+ ) } ms`,
110116 ) ;
111117
112118 console . info ( "\nEncrypted submission:" ) ;
@@ -134,7 +140,9 @@ async function runRetrieveDecryptAndConfirmFormSubmissions(
134140 ) ;
135141
136142 console . info (
137- `\nIntegrity verification result: ${ integrityVerificationResult ? "OK" : "INVALID" } ` ,
143+ `\nIntegrity verification result: ${
144+ integrityVerificationResult ? "OK" : "INVALID"
145+ } `,
138146 ) ;
139147
140148 await saveSubmissionLocally ( newFormSubmission . name , formSubmission ) ;
@@ -165,36 +173,61 @@ async function saveSubmissionLocally(
165173
166174 const submissionFolderPath = path . join ( "./" , submissionName ) ;
167175
168- await filesystem . mkdir ( submissionFolderPath , {
176+ await mkdir ( submissionFolderPath , {
169177 recursive : true ,
170178 } ) ;
171179
172- await filesystem . writeFile (
173- path . join ( `./ ${ submissionName } ` , "answers.json" ) ,
180+ await writeFile (
181+ path . join ( submissionFolderPath , "answers.json" ) ,
174182 submission . answers ,
175183 ) ;
176184
177185 if ( submission . attachments ) {
178186 console . info ( "\nSaving submission attachments...\n" ) ;
179187
180- for ( const attachment of submission . attachments ) {
181- const attachmentBuffer = Buffer . from (
182- attachment . base64EncodedContent ,
183- "base64" ,
184- ) ;
188+ await Promise . all (
189+ submission . attachments . map ( ( attachment ) =>
190+ downloadAndSaveAttachment ( attachment , submissionFolderPath ) ,
191+ ) ,
192+ ) ;
193+ }
194+
195+ console . info ( `\nSubmission saved in folder named '${ submissionFolderPath } '` ) ;
196+ }
185197
186- await filesystem . writeFile (
187- path . join ( `./${ submissionName } ` , attachment . name ) ,
188- attachmentBuffer ,
198+ async function downloadAndSaveAttachment (
199+ attachment : Attachment ,
200+ submissionFolderPath : string ,
201+ ) : Promise < void > {
202+ return axios
203+ . get < Readable > ( attachment . downloadLink , { responseType : "stream" } )
204+ . then ( ( response ) => {
205+ const writableStream = createWriteStream (
206+ path . join ( submissionFolderPath , attachment . name ) ,
189207 ) ;
190208
209+ return new Promise < void > ( ( resolve , reject ) => {
210+ writableStream . on ( "finish" , resolve ) ;
211+ writableStream . on ( "error" , reject ) ;
212+
213+ response . data . pipe ( writableStream ) ;
214+ } ) ;
215+ } )
216+ . then ( ( ) => {
191217 console . info (
192- `Submission attachment '${ attachment . name } ' has been saved ${ attachment . isPotentiallyMalicious ? "(flagged as potentially malicious)" : "" } ` ,
218+ `Submission attachment '${ attachment . name } ' has been saved ${
219+ attachment . isPotentiallyMalicious
220+ ? "(flagged as potentially malicious)"
221+ : ""
222+ } `,
193223 ) ;
194- }
195- }
196-
197- console . info ( `\nSubmission saved in folder named '${ submissionFolderPath } '` ) ;
224+ } )
225+ . catch ( ( error ) => {
226+ throw new Error (
227+ `Failed to download and save submission attachment ${ attachment . name } ` ,
228+ { cause : error } ,
229+ ) ;
230+ } ) ;
198231}
199232
200233async function runReportProblemWithFormSubmission (
@@ -240,8 +273,7 @@ async function runReportProblemWithFormSubmission(
240273}
241274
242275function loadPrivateApiKey ( ) : Promise < PrivateApiKey > {
243- return filesystem
244- . readdir ( "." )
276+ return readdir ( "." )
245277 . then ( ( files ) => {
246278 const validFiles = files . filter ( ( fileName ) =>
247279 fileName . endsWith ( "_private_api_key.json" ) ,
@@ -256,7 +288,7 @@ function loadPrivateApiKey(): Promise<PrivateApiKey> {
256288 return validFiles [ 0 ] ;
257289 } )
258290 . then ( ( privateApiKeyFileName ) => {
259- return filesystem . readFile ( `./${ privateApiKeyFileName } ` , {
291+ return readFile ( `./${ privateApiKeyFileName } ` , {
260292 encoding : "utf8" ,
261293 } ) ;
262294 } )
0 commit comments