// /** // * @swagger // * /api/endpoint/createmywatchedcases_api: // * post: // * tags: [Portal,Case] // * summary: Phase 2 // * description: Create my watched cases // * responses: // * 200: // * description: Success // */ import axios from "axios"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; import { relayGetData } from "../middleware/relayForwarding"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../auth/[...nextauth]"; import { azureHeaders } from "../../../actions/core/headers"; import { escapeODataString } from "../../../actions/core/guards"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; const GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const recordExists = async (incidentId, contactId, accessToken) => { const filter = `$filter=pinswg_WatchedCase/incidentid eq ${incidentId} and pinswg_Contact/contactid eq ${contactId}`; const queryUrl = `pinswg_watchlists?${filter}` + "&$select=pinswg_watchlistid,_pinswg_contact_value,_pinswg_watchedcase_value,pinswg_representationsubmitted"; const response = await relayGetData({ queryUrl, accessToken, requestOptionsBuilder: (token) => ({ headers: { Authorization: "Bearer " + token, Accept: "application/json" } }) }); const records = response?.data?.value; if (!Array.isArray(records)) { return { status: "invalid", records: [] }; } return { status: "found", records }; }; export default async function ApiProxy(req, res) { if (req.method !== "POST") { return respondError(res, { status: 405, code: "METHOD_NOT_ALLOWED", message: "Only POST is supported" }); } const session = await getServerSession(req, res, authOptions(req, res)); const authenticatedEmail = session?.user?.email; if ( typeof authenticatedEmail !== "string" || authenticatedEmail.trim().length === 0 ) { return respondError(res, { status: 401, code: "UNAUTHENTICATED", message: "Authentication required" }); } const data = req.body; const watchedCaseBind = data?.["pinswg_WatchedCase@odata.bind"]; const contactBind = data?.["pinswg_Contact@odata.bind"]; if ( typeof watchedCaseBind !== "string" || watchedCaseBind.length === 0 || typeof contactBind !== "string" || contactBind.length === 0 ) { return respondError(res, { status: 400, code: "WATCHED_CASE_BINDINGS_REQUIRED", message: "pinswg_WatchedCase and pinswg_Contact bindings are required" }); } const watchedCaseMatch = watchedCaseBind.match( /^\/incidents\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)$/i ); const contactMatch = contactBind.match( /^\/contacts\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)$/i ); if (!watchedCaseMatch || !contactMatch) { return respondError(res, { status: 400, code: "INVALID_BINDING_FORMAT", message: "Invalid odata.bind format for watched case or contact" }); } try { const token = await getToken(); const incidentId = watchedCaseMatch[1]; const contactId = contactMatch[1]; const escapedEmail = escapeODataString(authenticatedEmail); const contactQueryUrl = "contacts?$filter=emailaddress1 eq '" + escapedEmail + "' and statuscode eq 1&$select=contactid"; const contactResponse = await relayGetData({ queryUrl: contactQueryUrl, accessToken: token.access_token, requestOptionsBuilder: (accessToken) => azureHeaders(accessToken) }); const contacts = contactResponse?.data?.value; if ( !Array.isArray(contacts) || contacts.length !== 1 || typeof contacts[0]?.contactid !== "string" || !GUID_PATTERN.test(contacts[0].contactid.trim()) ) { return respondError(res, { status: 403, code: "CONTACT_IDENTITY_NOT_RESOLVED", message: "Request not permitted" }); } if ( contactId.trim().toLowerCase() !== contacts[0].contactid.trim().toLowerCase() ) { return respondError(res, { status: 403, code: "CONTACT_OWNERSHIP_MISMATCH", message: "Request not permitted" }); } const lookup = await recordExists( incidentId, contactId, token.access_token ); if (lookup.status === "invalid") { return respondError(res, { status: 500, code: "WATCHLIST_LOOKUP_FAILED", message: "Unable to verify existing watched-case records" }); } const records = lookup.records; const isRepresentationSubmission = data?.pinswg_representationsubmitted != null; // Validate every returned record before considering it for an update. const validRecords = records.every((record) => { const recordID = record?.pinswg_watchlistid; const recordContactID = record?._pinswg_contact_value; const recordCaseID = record?._pinswg_watchedcase_value; return ( typeof recordID === "string" && GUID_PATTERN.test(recordID) && typeof recordContactID === "string" && GUID_PATTERN.test(recordContactID) && typeof recordCaseID === "string" && GUID_PATTERN.test(recordCaseID) && recordContactID.toLowerCase() === contactId.toLowerCase() && recordCaseID.toLowerCase() === incidentId.toLowerCase() ); }); if (!validRecords) { return respondError(res, { status: 403, code: "WATCHLIST_OWNERSHIP_MISMATCH", message: "Request not permitted" }); } const watchingOnlyRecords = records.filter( (record) => record.pinswg_representationsubmitted == null ); let existingRecord; if (isRepresentationSubmission) { // Reuse a watching-only record for the first representation. // Never overwrite an earlier representation submission. existingRecord = watchingOnlyRecords.length === 1 ? watchingOnlyRecords[0] : null; } else { // Watching or subscribing: prefer a watching-only record. // Otherwise reuse an existing representation-associated record. existingRecord = watchingOnlyRecords[0] ?? records[0] ?? null; } const method = existingRecord ? "patch" : "post"; const queryUrl = existingRecord ? `pinswg_watchlists(${existingRecord.pinswg_watchlistid})` : "pinswg_watchlists"; const config = { method: method, url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), headers: { "OData-MaxVersion": "4.0", "OData-Version": "4.0", Accept: "application/json", Prefer: 'odata.include-annotations="*",return=representation', Authorization: "Bearer " + token.access_token, "Content-Type": "application/json" }, data: JSON.stringify({ ...data, "pinswg_Contact@odata.bind": `/contacts(${contacts[0].contactid.trim()})` }) }; const result = await axios(config); return respondSuccess(res, result.data); } catch (err) { consoleLogger(err); return respondError(res, { status: 400, code: "WATCHED_CASE_UPSERT_FAILED", message: "Failed to create or update watched case" }); } }