diff --git a/server/integrations/VSSTA/vsstaIntegrationRoute.js b/server/integrations/VSSTA/vsstaIntegrationRoute.js index c40fce462..4fa0b800f 100644 --- a/server/integrations/VSSTA/vsstaIntegrationRoute.js +++ b/server/integrations/VSSTA/vsstaIntegrationRoute.js @@ -5,25 +5,34 @@ const { GET_JOB_BY_RO_NUMBER_AND_SHOP_ID, INSERT_NEW_DOCUMENT } = require("../.. const { InstanceRegion } = require("../../utils/instanceMgr"); const client = require("../../graphql-client/graphql-client").client; -const S3_BUCKET = process.env.IMGPROXY_DESTINATION_BUCKET; +const S3_BUCKET = process.env?.IMGPROXY_DESTINATION_BUCKET; + +/** + * @description VSSTA integration route + * @type {string[]} + */ +const requiredParams = [ + "shop_id", + "ro_nbr", + "pdf_download_link", + "company_api_key", + "scan_type", + "scan_time", + "technician", + "year", + "make", + "model" +]; const vsstaIntegrationRoute = async (req, res) => { const { logger } = req; - try { - const requiredParams = [ - "shop_id", - "ro_nbr", - "pdf_download_link", - "company_api_key", - "scan_type", - "scan_time", - "technician", - "year", - "make", - "model" - ]; + if (!S3_BUCKET) { + logger.log("vssta-integration-missing-bucket", "error", "api", "vssta"); + return res.status(500).json({ error: "Improper configuration" }); + } + try { const missingParams = requiredParams.filter((param) => !req.body[param]); if (missingParams.length > 0) { @@ -54,25 +63,24 @@ const vsstaIntegrationRoute = async (req, res) => { const job = jobResult.jobs[0]; - logger.logger.info(`Found job with ID ${job.id} for RO number ${ro_nbr}`); - - // 2. Download the PDF from the provided link - logger.logger.info(`Downloading PDF from ${pdf_download_link}`); + // 2. Download the base64-encoded PDF string from the provided link const pdfResponse = await axios.get(pdf_download_link, { - responseType: "arraybuffer" - // headers: { - // "auth:token": company_api_key - // } + responseType: "text", // Expect base64 string + headers: { + "auth-token": company_api_key + } }); - // 3. Generate key for S3 + // 3. Decode the base64 string to a PDF buffer + const base64String = pdfResponse.data.replace(/^data:application\/pdf;base64,/, ""); + const pdfBuffer = Buffer.from(base64String, "base64"); + + // 4. Generate key for S3 const timestamp = Date.now(); const fileName = `VSSTA_${scan_type}_Scan_${timestamp}.pdf`; const s3Key = `${job.shopid}/${job.id}/${fileName.replace(/[^A-Z0-9]+/gi, "_")}-${timestamp}.pdf`; - // 4. Generate presigned URL for S3 upload - logger.logger.info(`Generating presigned URL for S3 key ${s3Key}`); - + // 5. Generate presigned URL for S3 upload const s3Client = new S3Client({ region: InstanceRegion() }); const putCommand = new PutObjectCommand({ @@ -84,45 +92,42 @@ const vsstaIntegrationRoute = async (req, res) => { const presignedUrl = await getSignedUrl(s3Client, putCommand, { expiresIn: 360 }); - // 5. Upload file to S3 - logger.logger.info(`Uploading PDF to S3 with key ${s3Key}`); - - await axios.put(presignedUrl, pdfResponse.data, { + // 6. Upload the decoded PDF to S3 + await axios.put(presignedUrl, pdfBuffer, { headers: { "Content-Type": "application/pdf" } }); - // 6. Create document record in database + // 7. Create document record in database const documentMeta = { - jobid: job.id, // Matches jobid (uuid, nullable) - uploaded_by: "VSSTA Integration", // Matches uploaded_by (text) - name: fileName, // Matches name (text, nullable) - key: s3Key, // Matches key (text, default: '0'::text) - // type: determineFileType("application/pdf"), // Matches type (text, nullable), using determineFileType - // Ask Patrick why determineFileType just returns image... - type: "application/pdf", // Matches type (text, nullable), - extension: "pdf", // Matches extension (text, nullable) - bodyshopid: job.shopid, // Matches bodyshopid (uuid, nullable) - size: pdfResponse.data.length, // Matches size (integer, default: 0) - takenat: scan_time // Matches takenat (timestamp with time zone, nullable) + jobid: job.id, + uploaded_by: "VSSTA Integration", + name: fileName, + key: s3Key, + type: "application/pdf", + extension: "pdf", + bodyshopid: job.shopid, + size: pdfBuffer.length, + takenat: scan_time }; const documentInsert = await client.request(INSERT_NEW_DOCUMENT, { docInput: [documentMeta] }); - if (documentInsert.insert_documents?.returning?.length > 0) { - logger.logger.info(`Document created with ID ${documentInsert.insert_documents.returning[0].id}`); - return res.status(200).json({ - message: "VSSTA integration successful", - documentId: documentInsert.insert_documents.returning[0].id - }); - } else { + // Reversed flow: check for error case + if (!documentInsert.insert_documents?.returning?.length) { logger.log(`vssta-integration-failed-to-create-document-record`, "error", "api", "vssta", { params: missingParams }); - return res.status(500).json({ error: "Failed to create document record" }); } + + // Success case + logger.logger.info(`Document created with ID ${documentInsert.insert_documents.returning[0].id}`); + return res.status(200).json({ + message: "VSSTA integration successful", + documentId: documentInsert.insert_documents.returning[0].id + }); } catch (error) { logger.log(`vssta-integration-general`, "error", "api", "vssta", { error: error?.message,