diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index fe08897ba..761550f1d 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2856,15 +2856,10 @@ query GET_BODYSHOP_BY_MERCHANTID($merchantID: String!) { // Define the GraphQL query to get a job by RO number and shop ID exports.GET_JOB_BY_RO_NUMBER_AND_SHOP_ID = ` - query GET_JOB_BY_RO_NUMBER_AND_SHOP_ID($roNumber: String!, $shopId: String!) { - jobs(where: {ro_number: {_eq: $roNumber}, shopid: {_eq: $shopId}}) { + query GET_JOB_BY_RO_NUMBER_AND_SHOP_ID($roNumber: String!, $shopId: uuid!) { + jobs(where: {ro_number: {_eq: $roNumber}, shopid: {_eq: $shopId}}, limit: 1) { id shopid - bodyshopid - bodyshop { - id - email - } } } `; diff --git a/server/integrations/VSSTA/vsstaIntegrationRoute.js b/server/integrations/VSSTA/vsstaIntegrationRoute.js index 77f5094b9..c40fce462 100644 --- a/server/integrations/VSSTA/vsstaIntegrationRoute.js +++ b/server/integrations/VSSTA/vsstaIntegrationRoute.js @@ -2,15 +2,14 @@ const axios = require("axios"); const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3"); const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); const { GET_JOB_BY_RO_NUMBER_AND_SHOP_ID, INSERT_NEW_DOCUMENT } = require("../../graphql-client/queries"); -const determineFileType = require("../../media/util/determineFileType"); const { InstanceRegion } = require("../../utils/instanceMgr"); const client = require("../../graphql-client/graphql-client").client; -// Assume these are configured environment variables or constants -const S3_BUCKET = process.env.S3_BUCKET || "your-s3-bucket-name"; +const S3_BUCKET = process.env.IMGPROXY_DESTINATION_BUCKET; const vsstaIntegrationRoute = async (req, res) => { const { logger } = req; + try { const requiredParams = [ "shop_id", @@ -28,7 +27,10 @@ const vsstaIntegrationRoute = async (req, res) => { const missingParams = requiredParams.filter((param) => !req.body[param]); if (missingParams.length > 0) { - logger.error(`Missing required parameters: ${missingParams.join(", ")}`); + logger.log(`vssta-integration-missing-param`, "error", "api", "vssta", { + params: missingParams + }); + return res.status(400).json({ error: "Missing required parameters", missingParams @@ -45,29 +47,31 @@ const vsstaIntegrationRoute = async (req, res) => { }); if (!jobResult.jobs || jobResult.jobs.length === 0) { - logger.error(`No job found for RO number ${ro_nbr} and shop ID ${shop_id}`); + logger.log(`vssta-integration-missing-ro`, "error", "api", "vssta"); + return res.status(404).json({ error: "Job not found" }); } const job = jobResult.jobs[0]; - logger.info(`Found job with ID ${job.id} for RO number ${ro_nbr}`); + + logger.logger.info(`Found job with ID ${job.id} for RO number ${ro_nbr}`); // 2. Download the PDF from the provided link - logger.info(`Downloading PDF from ${pdf_download_link}`); + logger.logger.info(`Downloading PDF from ${pdf_download_link}`); const pdfResponse = await axios.get(pdf_download_link, { - responseType: "arraybuffer", - headers: { - "auth:token": company_api_key - } + responseType: "arraybuffer" + // headers: { + // "auth:token": company_api_key + // } }); // 3. Generate key for S3 const timestamp = Date.now(); const fileName = `VSSTA_${scan_type}_Scan_${timestamp}.pdf`; - const s3Key = `${job.bodyshopid}/${job.id}/${fileName.replace(/[^A-Z0-9]+/gi, "_")}-${timestamp}.pdf`; + const s3Key = `${job.shopid}/${job.id}/${fileName.replace(/[^A-Z0-9]+/gi, "_")}-${timestamp}.pdf`; // 4. Generate presigned URL for S3 upload - logger.info(`Generating presigned URL for S3 key ${s3Key}`); + logger.logger.info(`Generating presigned URL for S3 key ${s3Key}`); const s3Client = new S3Client({ region: InstanceRegion() }); @@ -81,7 +85,8 @@ const vsstaIntegrationRoute = async (req, res) => { const presignedUrl = await getSignedUrl(s3Client, putCommand, { expiresIn: 360 }); // 5. Upload file to S3 - logger.info(`Uploading PDF to S3 with key ${s3Key}`); + logger.logger.info(`Uploading PDF to S3 with key ${s3Key}`); + await axios.put(presignedUrl, pdfResponse.data, { headers: { "Content-Type": "application/pdf" } }); @@ -92,12 +97,13 @@ const vsstaIntegrationRoute = async (req, res) => { 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 + // 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.bodyshopid, // Matches bodyshopid (uuid, 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) - description: `VSSTA ${scan_type} scan for ${year} ${make} ${model}, performed by ${technician} at ${scan_time}` // Not in schema, will be ignored by the database + takenat: scan_time // Matches takenat (timestamp with time zone, nullable) }; const documentInsert = await client.request(INSERT_NEW_DOCUMENT, { @@ -105,17 +111,24 @@ const vsstaIntegrationRoute = async (req, res) => { }); if (documentInsert.insert_documents?.returning?.length > 0) { - logger.info(`Document created with ID ${documentInsert.insert_documents.returning[0].id}`); + 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 { - logger.error("Failed to create document record"); + 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" }); } } catch (error) { - logger.error(`VSSTA integration error: ${error.message}`, error); + logger.log(`vssta-integration-general`, "error", "api", "vssta", { + error: error?.message, + stack: error?.stack + }); + return res.status(500).json({ error: error.message }); } };