diff --git a/.gitignore b/.gitignore index 6c2b14ec..8a8ed18a 100644 --- a/.gitignore +++ b/.gitignore @@ -58,10 +58,5 @@ CONTRIBUTING_AI.md GUARDRAILS.md ai-prompts/ context/ -memory-bank/ workflows/ AI_CONTEXT.md -memory-bank/activeContext.md -memory-bank/change-log.md -memory-bank/refactor-plan-actions-index.md -memory-bank/progress.md diff --git a/memory-bank/## 1) Prioritized debt list.md b/memory-bank/## 1) Prioritized debt list.md new file mode 100644 index 00000000..b31fedaf --- /dev/null +++ b/memory-bank/## 1) Prioritized debt list.md @@ -0,0 +1,54 @@ +## 1) Prioritized debt list + +1. Monolithic `actions/index.js` (over-coupled “god module”) +2. Inconsistent API error handling/response contracts across `pages/api/**` +3. Sensitive/verbose logging in app + API paths +4. Endpoint sprawl and duplicated proxy patterns (`pages/api/endpoint/*_api.js`) +5. i18n routing complexity in large rewrite maps +6. Sparse automated test coverage for high-risk flows +7. Legacy/stale commented patterns in critical files + +## 2) Impact of each item + +- **1) `actions/index.js` monolith** + - High blast radius for any change; weak modularity and hard ownership boundaries. + - Evidence: ~100+ exports mixing search, auth, account, file, PDF, notify, CRM concerns. + +- **2) API contract inconsistency** + - Clients must handle errors inconsistently; reliability and observability suffer. + - Evidence: many handlers return raw `res.status(400).json(error)` while others shape custom outputs. + +- **3) Verbose/sensitive logging** + - Privacy/security risk plus noisy telemetry. + - Evidence: 300+ `console.log` occurrences, including auth/email/upload-related contexts. + +- **4) Endpoint duplication** + - Maintenance cost and drift risk (validation/auth/error semantics diverge over time). + - Evidence: repeated patterns across many `_api.js` proxy handlers. + +- **5) i18n rewrite complexity** + - Higher risk of EN/CY divergence as routes evolve. + - Evidence: large manual rewrite map + separate i18n page namespace config. + +- **6) Sparse tests** + - Release confidence depends heavily on manual checks; slower, riskier change cadence. + +- **7) Stale patterns** + - Increased cognitive load and unclear source-of-truth behavior in some modules. + +## 3) Recommended next cleanup targets + +1. **Decompose `actions/index.js` by domain** (structure-only first, keep barrel exports). +2. **Add shared API handler utilities** for validation/error envelope; migrate top sensitive endpoints first. +3. **Implement logging redaction wrapper + policy** and remove direct logs in auth/email/file paths. +4. **Create high-risk smoke tests** (auth callback, search endpoint, upload negative-path, email failure-path). +5. **Extract proxy/integration helpers** (token/hash/header/retry) to reduce endpoint duplication. +6. **Add EN/CY parity CI checks** for route and locale key consistency. + +## 4) Runtime note (updated) + +- `server.js` and `server/server.js` exist in the repository but are **not the active production concern** for the current refactor stream. +- Current baseline runtime for this debt plan is the standard Next runtime path (`npm start`). +- Runtime canonicalization is therefore removed from the active prioritized debt list unless deployment evidence changes. + +If you want, I can convert this into a sprint-ready debt register (ID, owner, effort, risk reduction, target milestone). diff --git a/memory-bank/Absolutely — here’s a senior architectur.md b/memory-bank/Absolutely — here’s a senior architectur.md new file mode 100644 index 00000000..356cb597 --- /dev/null +++ b/memory-bank/Absolutely — here’s a senior architectur.md @@ -0,0 +1,94 @@ +Absolutely — here’s a senior architecture review of this repository. + +## Update: TASK22211 completed slice (2026-03-23) + +- Branch: `TASK22211-endpoint-search-document-contract-consistency` +- Scope completed: + - `pages/api/endpoint/getsearchdocumenthistory_api.js` + - `pages/api/endpoint/getsearchdocumenthistorypaged_api.js` + - `pages/api/endpoint/getsearchdocumentdetails_api.js` + - `pages/api/endpoint/getsearchdocumentdetailspaged_api.js` + - `pages/api/endpoint/getsearchdocumentTypes_api.js` + - `tests/phase21/endpoint-handler-contract.test.cjs` + - `memory-bank/change-log.md` +- Pattern applied: + - standardized structured error envelopes via `respondError` + - preserved existing success contract behavior via `respondSuccess` + - added explicit required-input guards for missing document/case/query inputs + - removed noisy direct `console.log` usage in touched handlers +- Validation snapshot: + - `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper 4/4 + - file-handler 11/11 + - email-handler 12/12 + - endpoint-handler 53/53 + - `npm run lint` -> warnings only (pre-existing react-hooks warnings) + +### Suggested next coherent slice + +- Endpoint contract consistency follow-on for case/myportal retrieval cluster still using raw error passthrough patterns, applying the same bounded refactor + phase21 test expansion model. + +## 1) Current architecture summary + +- **Platform shape:** Next.js 14 (pages router) + React 18, with a **custom Node server** and also a legacy Express-style server under `server/server.js`. +- **API layer:** Large `pages/api/**` surface (endpoint/file/email/admin/auth), many routes acting as thin proxies to upstream systems (CRM/relay/Azure). +- **State/auth:** Redux (`next-redux-wrapper` + persistence) and `next-auth` + Prisma adapter for account/session persistence. +- **i18n/routing:** EN/CY via `next-translate`, `i18n.js`, and many Welsh rewrites in `next.config.js`. +- **Integration-heavy domains:** Azure storage/queues, GOV.UK Notify, mapping embeds/libs, PDF generation. + +## 2) Strengths + +- **Clear high-level domain separation** by folders (`pages`, `components`, `store`, `actions`, `prisma`, `locales`). +- **Strong security intent** (CSP + secure headers + auth/session infrastructure in place). +- **Bilingual-first routing model** is explicit and robustly represented in config. +- **Operational integration maturity**: telemetry, notifications, document handling, and mapping already embedded. +- **Recently added governance docs** (`.clinerules`, `GUARDRAILS.md`, `CONTRIBUTING_AI.md`, `context/`, `memory-bank/`) materially improve consistency and delivery safety. + +## 3) Risks (scalability, maintainability, coupling, boundaries, operations, debt) + +1. **God-module risk in `actions/index.js` (very high maintainability/coupling risk)** + Hundreds of mixed responsibilities (auth helpers, search, uploads, notifications, case operations) create high fan-in/fan-out and regression blast radius. +2. **Boundary leakage between UI and integration concerns** + Frontend-facing actions are tightly coupled to relay/API details, hashes, token flow assumptions, and endpoint naming conventions. +3. **Inconsistent runtime topology / entrypoint ambiguity** + Both root `server.js` and `server/server.js` exist with different runtime behavior, increasing deployment confusion and operational drift risk. +4. **Logging/privacy exposure risk** + Multiple debug logs in sensitive paths (auth/email/payloads) risk leaking identifiers or confidential request data. +5. **API proliferation with naming inconsistency and limited shared contract enforcement** + Many `_api.js` endpoints with repeated patterns suggest technical debt and likely drift in validation/error handling semantics. +6. **Scalability bottlenecks in synchronous proxy patterns** + High reliance on serial external calls and heavy payload operations (search/doc/PDF flows) without strong evidence of caching, backpressure, or circuit-breaking. +7. **Test coverage fragility** + Sparse automated tests means confidence depends heavily on manual checks, slowing safe change velocity and increasing incident probability. +8. **i18n rewrite complexity growth** + Large rewrite maps increase cognitive load and chance of EN/CY divergence as route count expands. + +## 4) Prioritized recommendations + +1. **Decompose `actions/index.js` into domain modules (highest priority)** + - Split into `actions/search`, `actions/case`, `actions/account`, `actions/docs`, `actions/notify`, etc. + - Add a stable facade export to avoid immediate call-site churn. +2. **Establish explicit service boundaries for API integrations** + - Create integration adapters in `lib/services/*` (CRM/relay/azure/notify). + - Keep API routes as orchestrators with shared validation/error wrappers. +3. **Resolve server runtime canonically** + - Choose one entrypoint for dev/test/prod, deprecate the other, simplify scripts and ops docs. +4. **Implement secure logging policy + redaction utility** + - Centralized logger; block raw payload/token/user-data logging by default. +5. **Standardize API contract/error envelope** + - Shared response/error shape and input validation helper used by all sensitive endpoints. +6. **Introduce incremental architecture tests** + - Start with high-risk auth/file/email negative-path API tests. +7. **Tame i18n rewrite sprawl** + - Introduce parity checks (lint/CI) for EN/CY route + locale key synchronization. + +## 5) Low-risk next improvements (next 1–2 sprints) + +- Add a **single redaction helper** and replace highest-risk `console.log` usage first (auth/email/file flows). +- Add **API wrapper utility** for consistent try/catch + error response formatting, then migrate 5–10 high-traffic routes. +- Create `actions/` module split with **barrel exports** (no behavioral change, structure-only). +- Add **CI guard checks**: `npm run lint` + locale key parity + forbidden logging patterns in sensitive files. +- Decide and document **canonical startup path** in `context/runbook.md` and package scripts. +- Add a **small smoke test suite** for auth sign-in callback, public search endpoint, and one upload negative-path scenario. + +If you want, I can turn this into a concrete 30/60/90-day architecture hardening roadmap with owners, sequencing, and expected risk reduction per step. diff --git a/memory-bank/README.md b/memory-bank/README.md new file mode 100644 index 00000000..8665899b --- /dev/null +++ b/memory-bank/README.md @@ -0,0 +1,38 @@ +# Memory Bank Operating Protocol + +## Purpose + +Capture durable engineering knowledge for PEDW FrontEnd so AI agents and humans make consistent, safe decisions over time. + +## When to Update + +Update memory-bank entries when any of the following occur: + +1. A non-trivial architectural or implementation decision is made. +2. A recurring pattern is introduced or standardized. +3. A pitfall/incident/root cause is discovered. +4. A release introduces meaningful behavior/risk changes. +5. Open questions block safe delivery. + +## Who Updates + +- **AI agent:** must add or amend entries for non-trivial changes completed during a task. +- **Human engineer/reviewer:** validates or amends entries during PR review. +- **Tech lead/owner:** resolves open questions and marks decisions as accepted/superseded. + +## Required Metadata (for every new entry) + +- `date:` YYYY-MM-DD +- `author:` agent name or person +- `scope:` files/routes/features affected +- `type:` decision | pattern | pitfall | change | question | glossary +- `rationale:` why this matters +- `impact:` user/system/security/i18n/a11y implications +- `status:` proposed | accepted | superseded | open | resolved + +## Entry Quality Rules + +- Keep entries concise and specific to this repository. +- Link to concrete files/paths where possible. +- Do not include secrets, tokens, personal data, or sensitive payload examples. +- If uncertain, record assumptions explicitly and add to `open-questions.md`. diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md new file mode 100644 index 00000000..9e63b68c --- /dev/null +++ b/memory-bank/change-log.md @@ -0,0 +1,131 @@ +# Change Log (AI/Human Curated) + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: change +rationale: +impact: +status: completed|rolled-back|partial + +Summary: +Validation: +Follow-ups: +``` + +--- + +### CL-001: TASK22211 endpoint search-document contract consistency slice + +date: 2026-03-23 +author: Cline +scope: `pages/api/endpoint/{getsearchdocumenthistory_api,getsearchdocumenthistorypaged_api,getsearchdocumentdetails_api,getsearchdocumentdetailspaged_api,getsearchdocumentTypes_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue the endpoint contract-consistency stream by normalizing a coherent search-document handler cluster that still used raw error passthrough and noisy legacy logging patterns. +impact: Improved negative-path consistency and safer error contract handling in search-document endpoints while preserving success payload behavior. +status: completed + +Summary: + +- Branch created from `SIPS-Development`: `TASK22211-endpoint-search-document-contract-consistency`. +- Standardized five search-document handlers to `respondError`/`respondSuccess` usage. +- Added explicit required-input guards: + - `DOCUMENT_ID_REQUIRED` for history/historypaged + - `INCIDENT_ID_REQUIRED` for details/detailspaged/types + - `ORDER_BY_REQUIRED`, `FIELD_SORT_REQUIRED`, `SHOW_NUMBER_OF_RECORDS_REQUIRED` for details-paged query requirements +- Removed noisy direct logging in paged/details code paths. +- Preserved success contract patterns (pass-through or transformed payloads where already established). +- Expanded phase21 endpoint tests with missing-input, catch-path, and success parity assertions for this cluster. + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 11/11 + - email-handler: 12/12 + - endpoint-handler: 53/53 +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps` warnings; no new lint errors) + +Follow-ups: + +- Continue the next endpoint cluster using the same pattern (bounded slice + phase21 test expansion). +- Keep response success payloads contract-stable and avoid broad relay/auth refactors in this stream. + +### CL-002: TASK22211 endpoint token handler contract consistency slice + +date: 2026-03-23 +author: Cline +scope: `pages/api/endpoint/getToken.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Close out remaining non-standard endpoint contract handling by normalizing the legacy token endpoint to shared API response helpers and explicit error coding. +impact: Improved endpoint error consistency and test coverage for token acquisition failures while preserving successful token payload passthrough. +status: completed + +Summary: + +- Refactored `getToken.js` to use `respondSuccess` and `respondError` from `pages/api/middleware/apiResponse`. +- Removed legacy raw `res.status(...).json(...)`/bare status assignment pattern and dead logging artifacts. +- Added explicit catch-path contract: `TOKEN_FETCH_FAILED` with 400 status. +- Added endpoint phase21 tests for: + - success token payload passthrough + - catch-path error contract assertion + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 11/11 + - email-handler: 12/12 + - endpoint-handler: 147/147 +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps` warnings; no new lint errors) + +Follow-ups: + +- Remaining outlier API handler for this consistency stream is `pages/api/file/generateappealpdfcopy.js` (not yet on shared response helpers). + +### CL-003: TASK22211 endpoint contract-hardening stream backfill (all known slices) + +date: 2026-03-23 +author: Cline +scope: `pages/api/endpoint/*_api.js`, `pages/api/endpoint/getToken.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Backfill memory-bank traceability so the complete known TASK22211 contract-consistency stream is documented in one place now that memory-bank is being versioned. +impact: Improves governance/auditability of API contract hardening, makes rollout and rollback analysis easier, and records exactly which endpoint clusters were normalized. +status: completed + +Summary: + +- Backfilled all known TASK22211 slices currently on branch (in commit order): + - `b57f3de` search-document endpoint contracts + phase21 coverage + - `9af541a` my-portal retrieval endpoint contracts + - `b880364` basic search endpoint contracts + - `a106dea` DNS basic search endpoint contracts + - `b5a3a62` portal module + LPA case endpoint contracts + - `4601d7c` case detail endpoint contracts + - `2959c7d` delete/watched-case endpoint contracts + - `b59f13a` metadata + linked-case endpoint contracts + - `bcf03a6` form + publication endpoint contracts + - `e0e91c8` DNS + representation endpoint contracts + - `98e159d` case creation + media endpoint contracts + - `88e4586` advanced-search-paged endpoint contract + - `cb69bbe` case update + CRM task endpoint contracts + - `722ef98` hash + metadata endpoint contracts + - `134f99c` address-search endpoint contract + - `8b6ed73` new-appeal appeal-types endpoint contract + - `eec59e8` token endpoint contract handling +- Across the stream, handlers were standardized toward `respondSuccess`/`respondError`, required-input guards, and explicit negative-path error codes while preserving success payload compatibility. +- Phase21 endpoint contract suite was expanded incrementally alongside each slice. + +Validation: + +- Stream validation baseline (latest known run): + - `node tests/phase21/api-contract-slice1.test.cjs` -> pass (endpoint-handler 147/147) + - `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue with remaining non-standard API outlier(s), notably `pages/api/file/generateappealpdfcopy.js`. +- Keep future slices logged in this file at commit-time now that memory-bank is versioned. diff --git a/memory-bank/decisions.md b/memory-bank/decisions.md new file mode 100644 index 00000000..43fcc8de --- /dev/null +++ b/memory-bank/decisions.md @@ -0,0 +1,47 @@ +# Decisions (ADR-Lite) + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: decision +rationale: +impact: +status: proposed|accepted|superseded + +Decision: +Consequences: +Related: +``` + +--- + +### D-001: AI Governance Bundle Structure + +date: 2026-03-11 +author: Cline +scope: .clinerules, ai-prompts/, context/, memory-bank/ +type: decision +rationale: Establish a durable AI collaboration system aligned to repository risks and delivery model. +impact: Improves consistency, review quality, and safety for auth/i18n/public-service flows. +status: accepted + +Decision: +Adopt four artifact pillars: + +1. `.clinerules` for operating constraints, +2. `ai-prompts/` for reusable task templates, +3. `context/` for architecture/domain references, +4. `memory-bank/` for persistent project knowledge. + +Consequences: + +- AI/human contributors use a shared protocol. +- Non-trivial changes require memory-bank updates. + +Related: + +- `context/project-overview.md` +- `memory-bank/README.md` diff --git a/memory-bank/glossary.md b/memory-bank/glossary.md new file mode 100644 index 00000000..8f6933ca --- /dev/null +++ b/memory-bank/glossary.md @@ -0,0 +1,79 @@ +# Glossary + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: glossary +rationale: +impact: +status: active + +Term: +Definition: +Where used: +``` + +--- + +### G-001: DNS + +date: 2026-03-11 +author: Cline +scope: domain flows +type: glossary +rationale: Distinguishes DNS journey from general case search flows. +impact: Reduces implementation ambiguity in routing and UI behavior. +status: active + +Term: +DNS + +Definition: +Developments of National Significance journey and related case/search routes. + +Where used: + +- `pages/dns/**`, `pages/myportal/dns/**`, DNS-related search/detail pages + +### G-002: HYDRATE + +date: 2026-03-11 +author: Cline +scope: Redux store lifecycle +type: glossary +rationale: Critical to SSR/client state consistency. +impact: Prevents store regressions during refactors. +status: active + +Term: +HYDRATE + +Definition: +`next-redux-wrapper` action used to merge server-side state into client-side store. + +Where used: + +- `store/store.js` + +### G-003: Locale Parity + +date: 2026-03-11 +author: Cline +scope: i18n and routing +type: glossary +rationale: Core quality expectation for bilingual public service. +impact: Ensures consistent EN/CY experience. +status: active + +Term: +Locale parity + +Definition: +Equivalent behavior/content correctness across English (`en`) and Welsh (`cy`) for affected user journeys. + +Where used: + +- `locales/**`, `i18n.js`, `next.config.js`, user-facing pages/components diff --git a/memory-bank/open-questions.md b/memory-bank/open-questions.md new file mode 100644 index 00000000..6f2e8b95 --- /dev/null +++ b/memory-bank/open-questions.md @@ -0,0 +1,57 @@ +# Open Questions + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: question +rationale: +impact: +status: open|resolved + +Question: +Needed from: +Decision deadline: +``` + +--- + +### Q-001: Auth Logging Redaction Standard + +date: 2026-03-11 +author: Cline +scope: auth/email APIs and actions logging +type: question +rationale: Existing logging appears verbose in sensitive flows. +impact: Potential privacy/security exposure via operational logs. +status: open + +Question: +What mandatory redaction and logging policy should be enforced for auth/email/document flows? + +Needed from: +Security lead + application owner + +Decision deadline: +Before next auth/notification release + +### Q-002: Test Strategy Baseline for Sparse `tests/` + +date: 2026-03-11 +author: Cline +scope: testing strategy repository-wide +type: question +rationale: `tests/` currently appears minimal; validation relies heavily on manual checks. +impact: Increased regression risk and slower release confidence. +status: open + +Question: +What minimum automated coverage should be required per change type (feature, bug fix, high-risk integration)? + +Needed from: +Engineering lead + QA + +Decision deadline: +Before next major feature cycle diff --git a/memory-bank/patterns.md b/memory-bank/patterns.md new file mode 100644 index 00000000..8734e940 --- /dev/null +++ b/memory-bank/patterns.md @@ -0,0 +1,66 @@ +# Patterns + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: pattern +rationale: +impact: +status: accepted|superseded + +Pattern: +When to use: +Example paths: +``` + +--- + +### P-001: Thin Pages, Reusable Logic Elsewhere + +date: 2026-03-11 +author: Cline +scope: `pages/`, `components/`, `lib/`, `actions/` +type: pattern +rationale: Improve maintainability and reduce route-level complexity. +impact: Better testability and safer incremental changes. +status: accepted + +Pattern: +Keep route pages focused on composition and orchestration; place reusable behavior in `lib/`, `components/`, or `actions/`. + +When to use: + +- New features in existing routes +- Refactors reducing duplication across pages/components + +Example paths: + +- `pages/newappeal/**` +- `components/newappeal/**` +- `actions/index.js` + +### P-002: EN/CY Parity for User-Facing Changes + +date: 2026-03-11 +author: Cline +scope: `locales/`, `i18n.js`, `next.config.js`, user-facing pages +type: pattern +rationale: Service requires bilingual consistency and reliable route behavior. +impact: Prevents locale divergence and citizen confusion. +status: accepted + +Pattern: +Any user-facing change should include EN and CY text/route validation as part of done checks. + +When to use: + +- New/changed labels, messages, headings +- New/changed route aliases or rewrites + +Example paths: + +- `locales/en/**`, `locales/cy/**` +- `next.config.js`, `i18n.js` diff --git a/memory-bank/pitfalls.md b/memory-bank/pitfalls.md new file mode 100644 index 00000000..36aa35cd --- /dev/null +++ b/memory-bank/pitfalls.md @@ -0,0 +1,62 @@ +# Pitfalls + +## Entry Template + +``` +date: YYYY-MM-DD +author: +scope: +type: pitfall +rationale: +impact: +status: open|mitigated|resolved + +Pitfall: +How to detect: +How to avoid: +``` + +--- + +### PF-001: Locale Rewrite Drift + +date: 2026-03-11 +author: Cline +scope: `next.config.js`, `i18n.js`, user-facing routes +type: pitfall +rationale: Route/content parity can silently break across EN/CY if only one side is updated. +impact: Broken navigation and inconsistent bilingual experience. +status: mitigated + +Pitfall: +Adding or modifying a route in one locale without corresponding rewrite/translation updates. + +How to detect: + +- Manual navigation check for EN and CY aliases. +- Verify page namespace mappings in `i18n.js`. + +How to avoid: + +- Treat route + translation + rewrite as one change unit. + +### PF-002: Sensitive Logging in Auth/Notification Paths + +date: 2026-03-11 +author: Cline +scope: `pages/api/auth/[...nextauth].js`, `pages/api/email/**`, `actions/index.js` +type: pitfall +rationale: Debug logs in these paths can expose identifiers or callback tokens. +impact: Security and privacy risk. +status: open + +Pitfall: +Verbose console logs around auth URLs, email payloads, or user identifiers. + +How to detect: + +- Search changed files for `console.log` and inspect payload content. + +How to avoid: + +- Use redacted logging and avoid printing callback tokens/user-sensitive data. diff --git a/memory-bank/productContext.md b/memory-bank/productContext.md new file mode 100644 index 00000000..31bf49f0 --- /dev/null +++ b/memory-bank/productContext.md @@ -0,0 +1,37 @@ +# Product Context — PEDW FrontEnd + +## Primary user workflows + +1. **Public discovery** + - Search planning cases (basic, advanced, address, DNS). + - Open case summary pages and review documents/history. + +2. Authenticated portal (`/myportal/**`) + - Sign in via magic-link email. + - View dashboards/worklists (my cases, watched cases, awaiting submission, representations). + - Start or continue appeal/representation submission flows. + +3. **Role-shaped behavior** + - General users can create appeals and representations. + - LPA users get authority-scoped views and representation actions; raise-appeal behavior is restricted. + +## Core domain concepts + +- **Case / Incident**: core planning entity shown in search, detail, and portal journeys. +- **Appeal type**: controls collection/field behavior and some UI logic. +- **Representation window**: date-gated period controlling whether representation actions are available. +- **Watched case / email notifications**: user subscriptions to case changes. +- **DNS**: Developments of National Significance-specific journey and pages. + +## Business logic patterns visible in code + +- Case summary UI varies by appeal type and case attributes. +- Representation actions are conditionally shown by appeal type, role, and date windows. +- Locale-sensitive behavior affects links, routes, labels, and email template choice. +- New-case-reference notifications include preferred-language checks before template selection. + +## Important user-facing behaviors + +- Welsh route aliases are mapped in `next.config.js` and must remain in sync with page behavior. +- Breadcrumb/back-navigation behavior is actively maintained across search -> case -> portal routes. +- Document visibility/download flows are central to case transparency and must remain stable. diff --git a/memory-bank/projectbrief.md b/memory-bank/projectbrief.md new file mode 100644 index 00000000..b86c1a7d --- /dev/null +++ b/memory-bank/projectbrief.md @@ -0,0 +1,25 @@ +# Project Brief — PEDW FrontEnd + +## What this project is + +PEDW FrontEnd is a bilingual (English/Welsh) public-service web portal for Planning and Environment Decisions Wales. It supports public case discovery and authenticated portal workflows for planning appeals, representations, and DNS (Developments of National Significance) journeys. + +## Problem it solves + +- Gives citizens and stakeholders a single place to search planning cases and view case details/documents. +- Enables authenticated users to create/manage appeals and representations through structured online workflows. +- Provides role-aware dashboard experiences (e.g. general portal users and LPA users) without exposing internal CRM complexity. + +## Key users / use cases + +- **Public users:** search, filter, and view planning case information and documents. +- **Authenticated portal users:** manage account details, submit/continue appeals, submit representations, track watched and submitted items. +- **LPA users:** authority-scoped dashboard and representation-related actions. +- **Admin/support users:** review new appeals/documents and operational lists via admin endpoints/UI. + +## High-level success criteria + +1. Core public and portal journeys remain reliable (search, case detail, my portal, new appeal, representation). +2. Security-sensitive flows (auth/session, API relay/hash, file/document handling) remain intact. +3. EN/CY parity is maintained for routes and user-facing content. +4. Accessibility baseline remains acceptable for keyboard navigation, headings, labels, and focus behavior. diff --git a/memory-bank/refactor-backlog.md b/memory-bank/refactor-backlog.md new file mode 100644 index 00000000..5f7109d1 --- /dev/null +++ b/memory-bank/refactor-backlog.md @@ -0,0 +1,51 @@ +# Refactor Backlog (Maintainability + System Integrity) + +Last updated: 2026-03-12 + +## Priority 1 — Split `actions/index.js` by concern + +- **Problem:** Single high-coupling module mixes API clients, hash/token helpers, logging, file/email/domain operations. +- **Why it matters:** Hard to reason about changes; high regression risk from unrelated edits. +- **Target outcome:** Small modules with explicit boundaries (e.g. `relayClient`, `fileClient`, `notifyClient`, `caseService`, `authService`). +- **Initial scope:** create façade layer first, then migrate call sites incrementally. +- **Detailed plan:** `memory-bank/refactor-plan-actions-index.md` +- **Test gate:** add unit regression tests + coverage reporting as part of this refactor. + +## Priority 2 — Centralize relay/hash/token forwarding logic + +- **Problem:** `pages/api/endpoint/**` and related routes duplicate request-signing + token + forwarding behavior. +- **Why it matters:** Security-sensitive drift and inconsistent error handling. +- **Target outcome:** Shared relay utility enforcing one signing contract, one timeout/retry policy, one error map, one redacted logging strategy. +- **Initial scope:** pilot on a small endpoint group, then roll out pattern. + +## Priority 3 — Extract breadcrumb/back-link route-state rules + +- **Problem:** Navigation logic in `components/breadcrumbs.js` and case summary is branch-heavy and query-dependent. +- **Why it matters:** Frequent regressions in search -> case -> myportal paths. +- **Target outcome:** Pure route-state helpers with table-driven tests; UI component mainly renders output. +- **Initial scope:** isolate `va/adv/ads/key` decision matrix first. + +## Priority 4 — Standardize security guards in `pages/api/file/**` + +- **Problem:** Hash/guard enforcement appears inconsistent across file endpoints. +- **Why it matters:** Uneven protection for document upload/download/delete paths. +- **Target outcome:** Shared guard middleware for hash/auth/input validation + consistent negative-path responses. +- **Initial scope:** enforce a common pre-handler contract on high-risk routes first. + +## Priority 5 — Establish minimum automated regression baseline + +- **Problem:** Limited automated tests for high-risk logic. +- **Why it matters:** Repeated regressions and heavy manual verification burden. +- **Target outcome:** Focused test suite for pure logic and high-risk decisions. +- **Initial scope:** + 1. breadcrumb decision matrix + 2. representation eligibility/date windows + 3. hash utility behavior + 4. locale rewrite mapping sanity checks + +## Sequencing recommendation + +1. Priorities 2 + 4 (security/integrity foundation) +2. Priority 3 (high-change regression hotspot) +3. Priority 1 (structural maintainability) +4. Priority 5 (continuous safety net, starts early and expands) diff --git a/memory-bank/systemPatterns.md b/memory-bank/systemPatterns.md new file mode 100644 index 00000000..13606728 --- /dev/null +++ b/memory-bank/systemPatterns.md @@ -0,0 +1,41 @@ +# System Patterns — PEDW FrontEnd + +## Architecture style + +- Next.js 14 (Pages Router) monolith with route handlers in `pages/` and API handlers in `pages/api/**`. +- Mix of SSR/client rendering with Redux hydration (`HYDRATE`) and client persistence. +- Integration-heavy backend-for-frontend pattern: many internal API routes proxy to external services. + +## Module boundaries + +- `pages/`: route composition and API endpoints. +- `components/`: UI feature modules (case, search, dns, myportal, account, admin, mapping). +- `actions/`: shared API client helpers, token/header/hash helpers, and side-effect utilities. +- `lib/`: domain/form helper logic. +- `store/`: Redux reducers, wrapper, and persistence config. +- `prisma/`: auth/session persistence schema. + +## API patterns + +- Endpoint naming commonly uses `*_api.js` under `pages/api/endpoint/`, `pages/api/file/`, `pages/api/admin/`. +- Recurring endpoint implementation pattern: + 1. build query path, + 2. create HMAC hash (`hash` query param), + 3. fetch OAuth token, + 4. forward to relay URL, + 5. return normalized JSON / error. +- Swagger annotations are embedded in many API files for route documentation. + +## Data and integration boundaries + +- **Auth/session persistence**: Prisma models (`User`, `Account`, `Session`, `VerificationToken`) on SQL Server. +- **Case/business data**: fetched via API proxy routes to Dynamics/relay endpoints (not via Prisma business models). +- **File/document handling**: Azure blob-backed APIs under `pages/api/file/**`, including hash checks on sensitive routes. + +## Internal conventions and recurring implementation choices + +- Bilingual route mapping through `next.config.js` rewrites plus `i18n.js` page namespace mapping. +- Security headers are split across `middleware.js` (CSP + runtime security headers) and `next.config.js` static headers. +- Breadcrumb/back-link logic is centralized in `components/breadcrumbs.js` and depends on route/query state. +- Case summary behavior is appeal-type driven with branching UI/eligibility logic in `components/case/summary.js`. +- Proxy and direct variants coexist for some APIs (`*proxy_api.js`), so behavior parity must be checked when changing one side. diff --git a/memory-bank/techContext.md b/memory-bank/techContext.md new file mode 100644 index 00000000..1913be50 --- /dev/null +++ b/memory-bank/techContext.md @@ -0,0 +1,43 @@ +# Tech Context — PEDW FrontEnd + +## Languages and framework + +- JavaScript-first codebase (TypeScript tooling present). +- Next.js `^14.2.28` with Pages Router. +- React `^18.2.0`. + +## Core libraries in active use + +- Auth: `next-auth` + Prisma adapters. +- Data/state: `redux`, `react-redux`, `next-redux-wrapper`, `redux-persist`, `redux-thunk`, `redux-form`. +- Data access/integration: `axios`, `crypto-js`, `jsonpath-plus`. +- i18n: `next-translate`, locale config in `i18n.js`. +- Storage/infrastructure integrations: `@azure/storage-blob`, `@azure/storage-queue`, `@azure/identity`. +- Notifications/telemetry: `notifications-node-client` (GOV.UK Notify), `applicationinsights`. +- PDF/maps: `@react-pdf/renderer`, `react-pdf`, `leaflet`, `react-leaflet`, `google-map-react`. + +## Runtime and configuration + +- Standard app start script uses Next runtime (`npm start`). +- Additional custom server entries exist (`server.js`, `server/server.js`) but are currently treated as non-active for the refactor baseline unless deployment evidence indicates otherwise. +- Environment-driven config for auth, relay/API roots, hash key, notify key, and database URL. + +## Tooling and quality gates + +- Linting: `npm run lint` (`next lint`). +- Formatting conventions from `.prettierrc`: 4 spaces, no trailing commas. +- Prisma client generation script available: `npm run prisma:generate`. + +## CI/CD and deployment artifacts + +- `azure-pipelines.yml` exists (legacy-looking Node 10 build pipeline). +- `Jenkinsfile` exists (Node 20 + Docker build/push/deploy flow). +- `Dockerfile` exists (Node 20 Alpine runtime). +- Repository contains mixed deployment artifacts; active production path should be treated as environment-dependent unless confirmed. + +## Local development workflow + +- `npm install` +- `npm run dev` +- `npm run lint` +- `npm run build && npm start` for production-like verification diff --git a/pages/api/endpoint/createcase_api.js b/pages/api/endpoint/createcase_api.js index fcee4bf6..469b9e3c 100644 --- a/pages/api/endpoint/createcase_api.js +++ b/pages/api/endpoint/createcase_api.js @@ -42,65 +42,109 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; -import _ from "lodash"; +import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getCaseBlob } from "../../../actions/azurestorage"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var createCaseBody = req.body; - var contactid = req.query.contactid; - var appealTypeId = req.query.appealTypeId; - var containerName = req.query.containername; - var lpaID = req.query.lpaID; - var queryUrl = "incidents"; - var token = await getToken(); + const createCaseBody = req.body; + const contactid = req.query.contactid; + const appealTypeId = req.query.appealTypeId; + const containerName = req.query.containername; + const lpaID = req.query.lpaID; + const queryUrl = "incidents"; - var data = { - "title": "insertion test case", - "caseorigincode": 3, - "servicestage": 1, - "customerid_contact@odata.bind": "/contacts(" + contactid + ")", - "pinswg_appealcasetype": appealTypeId, - "pinswg_AssociatedLPA@odata.bind": "/accounts(" + lpaID + ")" - }; - var newData = Object.assign(data, createCaseBody); + if (!createCaseBody || typeof createCaseBody !== "object") { + return respondError(res, { + status: 400, + code: "CASE_PAYLOAD_REQUIRED", + message: "Case payload is required" + }); + } + if (typeof contactid !== "string" || contactid.trim().length === 0) { + return respondError(res, { + status: 400, + code: "CONTACT_ID_REQUIRED", + message: "contactid is required" + }); + } + if (typeof appealTypeId !== "string" || appealTypeId.trim().length === 0) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_ID_REQUIRED", + message: "appealTypeId is required" + }); + } + if ( + typeof containerName !== "string" || + containerName.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CONTAINER_NAME_REQUIRED", + message: "containername is required" + }); + } + if (typeof lpaID !== "string" || lpaID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "LPA_ID_REQUIRED", + message: "lpaID is required" + }); + } - delete newData.lpaTypes; - delete newData.appealTypes; + try { + const token = await getToken(); - console.log("/////Create Case:\n", newData, "\n//////////////"); + const data = { + "title": "insertion test case", + "caseorigincode": 3, + "servicestage": 1, + "customerid_contact@odata.bind": "/contacts(" + contactid + ")", + "pinswg_appealcasetype": appealTypeId, + "pinswg_AssociatedLPA@odata.bind": "/accounts(" + lpaID + ")" + }; + const newData = Object.assign(data, createCaseBody); - var config = { - method: "post", - 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(newData) - }; - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : axios(config) - .then(({ data }) => { - getCaseBlob(containerName, data.ticketnumber, createCaseBody); + delete newData.lpaTypes; + delete newData.appealTypes; - res.status(200).json(data); - }) - .catch((error) => { - //consoleLogger(error); - res.status(400).json(error); - }); + console.log("/////Create Case:\n", newData, "\n//////////////"); - return apiResponse; + const config = { + method: "post", + 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(newData) + }; + + const { data: responseData } = await axios(config); + await getCaseBlob( + containerName, + responseData.ticketnumber, + createCaseBody + ); + + return respondSuccess(res, responseData); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_CREATE_FAILED", + message: "Failed to create case" + }); + } } diff --git a/pages/api/endpoint/createcrmtask_api.js b/pages/api/endpoint/createcrmtask_api.js index 37dca717..af2f66b1 100644 --- a/pages/api/endpoint/createcrmtask_api.js +++ b/pages/api/endpoint/createcrmtask_api.js @@ -42,35 +42,35 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; -import _ from "lodash"; import { isNonEmptyString, sanitizeString } from "../../../actions/core/guards"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; -import { getCaseBlob } from "../../../actions/azurestorage"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var createTaskBody = req.body; + const createTaskBody = req.body; const contactEmail = sanitizeString(createTaskBody?.contactEmail); const contactSubject = sanitizeString(createTaskBody?.contactSubject); const contactBody = sanitizeString(createTaskBody?.contactbody); if (!isNonEmptyString(contactEmail) || !isNonEmptyString(contactSubject)) { - return res.status(400).json({ - error: "contactEmail and contactSubject are required" + return respondError(res, { + status: 400, + code: "CONTACT_EMAIL_AND_SUBJECT_REQUIRED", + message: "contactEmail and contactSubject are required" }); } - var queryUrl = "tasks"; - var token = await getToken(); + const queryUrl = "tasks"; + const token = await getToken(); - let contactValue = createTaskBody.typeOfContact; + const contactValue = createTaskBody.typeOfContact; const teamMap = { plq: "ec48450c-7e26-ec11-a97f-00224800e98c", //PET @@ -99,7 +99,7 @@ export default async function ApiProxy(req, res) { //console.log("/////Create Task:\n", payload, "\n//////////////"); - var config = { + const config = { method: "post", url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), headers: { @@ -117,11 +117,13 @@ export default async function ApiProxy(req, res) { try { const { data } = await axios(config); - return res.status(200).json(data); + return respondSuccess(res, data); } catch (error) { consoleLogger(error); - return res - .status(400) - .json({ error: "Failed to create CRM task", details: error }); + return respondError(res, { + status: 400, + code: "CRM_TASK_CREATE_FAILED", + message: "Failed to create CRM task" + }); } } diff --git a/pages/api/endpoint/createwatchedcases_api.js b/pages/api/endpoint/createwatchedcases_api.js index b1092ef4..55662558 100644 --- a/pages/api/endpoint/createwatchedcases_api.js +++ b/pages/api/endpoint/createwatchedcases_api.js @@ -11,10 +11,10 @@ // */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -83,51 +83,65 @@ export default async function ApiProxy(req, res) { typeof contactBind !== "string" || contactBind.length === 0 ) { - return res.status(400).json(); + return respondError(res, { + status: 400, + code: "WATCHED_CASE_BINDINGS_REQUIRED", + message: + "pinswg_WatchedCase and pinswg_Contact bindings are required" + }); } const watchedCaseMatch = watchedCaseBind.match(/\(([^)]+)\)/); const contactMatch = contactBind.match(/\(([^)]+)\)/); if (!watchedCaseMatch || !contactMatch) { - return res.status(400).json(); - } - - const token = await getToken(); - - const incidentId = watchedCaseMatch[1]; - const contactId = contactMatch[1]; - - const existingRecord = await recordExists(incidentId, contactId, token); - - let method, queryUrl; - - if (existingRecord) { - method = "patch"; - queryUrl = `pinswg_watchlists(${existingRecord.pinswg_watchlistid})`; // adjust with your primary key logical name - } else { - method = "post"; - queryUrl = "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) - }; - - return axios(config) - .then(({ data }) => res.status(200).json(data)) - .catch((err) => { - consoleLogger(err); - res.status(400).json(err.response?.data || err); + 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 existingRecord = await recordExists(incidentId, contactId, token); + + let method, queryUrl; + + if (existingRecord) { + method = "patch"; + queryUrl = `pinswg_watchlists(${existingRecord.pinswg_watchlistid})`; + } else { + method = "post"; + queryUrl = "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) + }; + + 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" + }); + } } diff --git a/pages/api/endpoint/deleteawaitingsubmissions_api.js b/pages/api/endpoint/deleteawaitingsubmissions_api.js index 64b8d0b6..9128477b 100644 --- a/pages/api/endpoint/deleteawaitingsubmissions_api.js +++ b/pages/api/endpoint/deleteawaitingsubmissions_api.js @@ -11,39 +11,51 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentID; - var token = await getToken(); - var queryUrl = "incidents(" + incidentID + ")"; + const incidentID = req.query.incidentID; - var config = { - method: "delete", - 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" - } - }; - - return axios(config) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" }); + } + + try { + const token = await getToken(); + const queryUrl = "incidents(" + incidentID + ")"; + + const config = { + method: "delete", + 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" + } + }; + + const { data } = await axios(config); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "AWAITING_SUBMISSION_DELETE_FAILED", + message: "Failed to delete awaiting submission" + }); + } } diff --git a/pages/api/endpoint/deletemyrepresentations_api.js b/pages/api/endpoint/deletemyrepresentations_api.js index aff512fe..62e02b46 100644 --- a/pages/api/endpoint/deletemyrepresentations_api.js +++ b/pages/api/endpoint/deletemyrepresentations_api.js @@ -11,40 +11,56 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var myRepresentationsID = req.query.myRepresentationsID; - var token = await getToken(); - var queryUrl = "pinswg_representationses(" + myRepresentationsID + ")"; + const myRepresentationsID = req.query.myRepresentationsID; - var config = { - method: "delete", - 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" - } - }; - - return axios(config) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + if ( + typeof myRepresentationsID !== "string" || + myRepresentationsID.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "MY_REPRESENTATION_ID_REQUIRED", + message: "myRepresentationsID is required" }); + } + + try { + const token = await getToken(); + const queryUrl = + "pinswg_representationses(" + myRepresentationsID + ")"; + + const config = { + method: "delete", + 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" + } + }; + + const { data } = await axios(config); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "MY_REPRESENTATION_DELETE_FAILED", + message: "Failed to delete representation" + }); + } } diff --git a/pages/api/endpoint/deletewatchedcases_api.js b/pages/api/endpoint/deletewatchedcases_api.js index 3972ccff..5c59ecff 100644 --- a/pages/api/endpoint/deletewatchedcases_api.js +++ b/pages/api/endpoint/deletewatchedcases_api.js @@ -11,45 +11,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var watchedCaseID = req.query.watchedCaseID; + const watchedCaseID = req.query.watchedCaseID; - if (typeof watchedCaseID === "undefined" || watchedCaseID.length === 0) { - return res.status(400).json(); + if ( + typeof watchedCaseID !== "string" || + watchedCaseID.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "WATCHED_CASE_ID_REQUIRED", + message: "watchedCaseID is required" + }); } - var token = await getToken(); - var queryUrl = "pinswg_watchlists(" + watchedCaseID + ")"; + try { + const token = await getToken(); + const queryUrl = "pinswg_watchlists(" + watchedCaseID + ")"; - var config = { - method: "delete", - 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" - } - }; + const config = { + method: "delete", + 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" + } + }; - return axios(config) - .then(({ data }) => { - //console.log("deleted watched case - " + watchedCaseID); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + const { data } = await axios(config); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "WATCHED_CASE_DELETE_FAILED", + message: "Failed to delete watched case" }); + } } diff --git a/pages/api/endpoint/deletewatchedcasesproxy_api.js b/pages/api/endpoint/deletewatchedcasesproxy_api.js index a9208db1..4aa4466a 100644 --- a/pages/api/endpoint/deletewatchedcasesproxy_api.js +++ b/pages/api/endpoint/deletewatchedcasesproxy_api.js @@ -18,61 +18,50 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; -const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`; +const BASE_URL = process.env.API_ROOT || "http://localhost:3000"; export default async function ApiProxy(req, res) { - var watchedCaseID = req.query.watchedCaseID; - var token = await getToken(); + const watchedCaseID = req.query.watchedCaseID; - var queryUrl = - "/api/endpoint/deletewatchedcases_api?watchedCaseID=" + watchedCaseID; + if ( + typeof watchedCaseID !== "string" || + watchedCaseID.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "WATCHED_CASE_ID_REQUIRED", + message: "watchedCaseID is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + const queryUrl = + "/api/endpoint/deletewatchedcases_api?watchedCaseID=" + + watchedCaseID; + + const { data } = await axios.get( BASE_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - // data.value.forEach(function (element) { - // element.ticketnumber = element.pinswg_WatchedCase?.ticketnumber; - // element.pinswg_title = - // element[ - // "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" - // ]; + ); - // element[ - // "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - // ] = - // element.pinswg_WatchedCase?.[ - // "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - // ]; - // element._pinswg_associatedlpa_value = - // element.pinswg_WatchedCase?._pinswg_associatedlpa_value; - // element[ - // "_ownerid_value@OData.Community.Display.V1.FormattedValue" - // ] = - // element.pinswg_WatchedCase?.[ - // "_ownerid_value@OData.Community.Display.V1.FormattedValue" - // ]; - // element._ownerid_value = - // element.pinswg_WatchedCase?._ownerid_value; - - // delete element.pinswg_WatchedCase; - // }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "WATCHED_CASE_PROXY_DELETE_FAILED", + message: "Failed to delete watched case via proxy" }); + } } diff --git a/pages/api/endpoint/getToken.js b/pages/api/endpoint/getToken.js index 9e2f6ca2..be712f67 100644 --- a/pages/api/endpoint/getToken.js +++ b/pages/api/endpoint/getToken.js @@ -13,41 +13,35 @@ * description: Success */ import axios from "axios"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; export default async function ApiProxy(req, res) { - const accessTokenEndpoint = process.env.ACCESS_TOKEN_ENDPOINT; - const tenantId = process.env.TENANT; - const tokenBody = - "grant_type=" + - process.env.GRANT_TYPE + - "&client_id=" + - process.env.CLIENT_ID + - "&client_secret=" + - process.env.CLIENT_SECRET + - "&scope=" + - "https://" + - process.env.RELAYURI + - "/.default"; + try { + const accessTokenEndpoint = process.env.ACCESS_TOKEN_ENDPOINT; + const tenantId = process.env.TENANT; + const tokenBody = + "grant_type=" + + process.env.GRANT_TYPE + + "&client_id=" + + process.env.CLIENT_ID + + "&client_secret=" + + process.env.CLIENT_SECRET + + "&scope=" + + "https://" + + process.env.RELAYURI + + "/.default"; - var config = { - method: "post", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - "Cache-Control": "no-cache", - }, - url: `${accessTokenEndpoint}${tenantId}/oauth2/v2.0/token`, - body: tokenBody, - }; + const { data } = await axios.post( + `${accessTokenEndpoint}${tenantId}/oauth2/v2.0/token`, + tokenBody + ); - console.log("get token case:", config); - - return axios - .post(`${accessTokenEndpoint}${tenantId}/oauth2/v2.0/token`, tokenBody) - .then(({ data }) => { - console.log; - res.status(200).json(data); - }) - .catch((error) => { - res.status(400); + return respondSuccess(res, data); + } catch (error) { + return respondError(res, { + status: 400, + code: "TOKEN_FETCH_FAILED", + message: "Unable to fetch token" }); + } } diff --git a/pages/api/endpoint/getadvancedsearchpaged_api.js b/pages/api/endpoint/getadvancedsearchpaged_api.js index 1422f748..915efdeb 100644 --- a/pages/api/endpoint/getadvancedsearchpaged_api.js +++ b/pages/api/endpoint/getadvancedsearchpaged_api.js @@ -48,33 +48,69 @@ // */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; -import { - azureHeadersPagedCustom, - azureHeadersPaged, - azureHeaders -} from "../../../actions/core/headers"; +import { azureHeadersPagedCustom } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchstring; - var pageNumber = req.query.pageNumber; - var token = await getToken(); + const rawSearchString = req.query.searchstring; + const pageNumber = req.query.pageNumber; + const orderby = req.query.orderby; + const fieldSort = req.query.fieldSort; + const showNumberOfRecords = req.query.showNumberOfRecords; - var orderby = req.query.orderby; - var fieldSort = req.query.fieldSort; - var showNumberOfRecords = req.query.showNumberOfRecords; + if ( + typeof rawSearchString !== "string" || + rawSearchString.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchstring is required" + }); + } + if (typeof orderby !== "string" || orderby.trim().length === 0) { + return respondError(res, { + status: 400, + code: "ORDER_BY_REQUIRED", + message: "orderby is required" + }); + } + if (typeof fieldSort !== "string" || fieldSort.trim().length === 0) { + return respondError(res, { + status: 400, + code: "FIELD_SORT_REQUIRED", + message: "fieldSort is required" + }); + } + if ( + typeof showNumberOfRecords !== "string" || + showNumberOfRecords.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "SHOW_NUMBER_OF_RECORDS_REQUIRED", + message: "showNumberOfRecords is required" + }); + } - searchString = _.isEmpty(searchString) - ? searchString - : JSON.parse(decodeURI(searchString)); + let searchString; + try { + searchString = JSON.parse(decodeURI(rawSearchString)); + } catch (error) { + return respondError(res, { + status: 400, + code: "INVALID_SEARCH_STRING", + message: "searchstring must be valid encoded JSON" + }); + } var queryString = ""; @@ -146,84 +182,81 @@ export default async function ApiProxy(req, res) { console.log("adv qu: ", queryUrl); - var apiResponse = _.isEmpty(searchString) - ? res.status(400).json() - : axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom( - token.access_token, - showNumberOfRecords - ) - ) - .then(async ({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); + try { + const token = await getToken(); + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPagedCustom(token.access_token, showNumberOfRecords) + ); - if (_.has(searchString, "projecttype")) { - // await updateValueArray(data, token); - // async function updateValueArray(data, token) { - // for (let i = 0; i < data.value.length; i++) { - // const item = data.value[i]; - // try { - // // Axios call using the `incidentid` to fetch additional data - // const response = await axios.get( - // WEBAPI_URL + - // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + - // item.incidentid + - // " and _pinswg_projecttype_value eq " + - // searchString.projecttype + - // "&$select=_pinswg_projecttype_value" + - // hashAPIPath( - // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + - // item.incidentid + - // " and _pinswg_projecttype_value eq " + - // searchString.projecttype + - // "&$select=_pinswg_projecttype_value" - // ), - // azureHeadersPaged(token.access_token) - // ); - // // Assuming the response contains the additional data you want to add - // // console.log(response.data.value[0]); - // console.log(response.data.value); - // if ( - // response.data.value.length > 0 && - // response.data.value[0] - // ._pinswg_projecttype_value != null - // ) { - // Object.assign( - // item, - // response.data.value[0] - // ); // Update item with new data - // } else { - // console.log( - // `No project type found for incident ID ${item.incidentid}` - // ); - // } - // } catch (error) { - // console.error( - // `Error fetching data for incident ID ${item.incidentid}:`, - // error - // ); - // } - // } - // } - // data.value = data.value.filter( - // (item) => item._pinswg_projecttype_value != null - // ); - // data["@odata.count"] = data.value.length; - } + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }); + if (_.has(searchString, "projecttype")) { + // await updateValueArray(data, token); + // async function updateValueArray(data, token) { + // for (let i = 0; i < data.value.length; i++) { + // const item = data.value[i]; + // try { + // // Axios call using the `incidentid` to fetch additional data + // const response = await axios.get( + // WEBAPI_URL + + // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + + // item.incidentid + + // " and _pinswg_projecttype_value eq " + + // searchString.projecttype + + // "&$select=_pinswg_projecttype_value" + + // hashAPIPath( + // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + + // item.incidentid + + // " and _pinswg_projecttype_value eq " + + // searchString.projecttype + + // "&$select=_pinswg_projecttype_value" + // ), + // azureHeadersPaged(token.access_token) + // ); + // // Assuming the response contains the additional data you want to add + // // console.log(response.data.value[0]); + // console.log(response.data.value); + // if ( + // response.data.value.length > 0 && + // response.data.value[0] + // ._pinswg_projecttype_value != null + // ) { + // Object.assign( + // item, + // response.data.value[0] + // ); // Update item with new data + // } else { + // console.log( + // `No project type found for incident ID ${item.incidentid}` + // ); + // } + // } catch (error) { + // console.error( + // `Error fetching data for incident ID ${item.incidentid}:`, + // error + // ); + // } + // } + // } + // data.value = data.value.filter( + // (item) => item._pinswg_projecttype_value != null + // ); + // data["@odata.count"] = data.value.length; + } - return apiResponse; + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "ADVANCED_SEARCH_PAGED_FETCH_FAILED", + message: "Failed to fetch advanced search paged results" + }); + } } // export default async function ApiProxy(req, res) { diff --git a/pages/api/endpoint/getappealid_api.js b/pages/api/endpoint/getappealid_api.js index ed1fefd8..d01ce523 100644 --- a/pages/api/endpoint/getappealid_api.js +++ b/pages/api/endpoint/getappealid_api.js @@ -29,40 +29,78 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var caseReference = req.query.caseReference; - var updateFormCollection = req.query.updateFormCollection; - var primaryAttribute = req.query.primaryAttribute; - var token = await getToken(); + const caseReference = req.query.caseReference; + const updateFormCollection = req.query.updateFormCollection; + const primaryAttribute = req.query.primaryAttribute; - const queryUrl = - updateFormCollection + - "?$count=true&$select=_" + - primaryAttribute + - "s_value&$filter=pinswg_name eq '" + - caseReference + - "'"; + if ( + typeof caseReference !== "string" || + caseReference.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CASE_REFERENCE_REQUIRED", + message: "caseReference is required" + }); + } - return axios - .get( + if ( + typeof updateFormCollection !== "string" || + updateFormCollection.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "UPDATE_FORM_COLLECTION_REQUIRED", + message: "updateFormCollection is required" + }); + } + + if ( + typeof primaryAttribute !== "string" || + primaryAttribute.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "PRIMARY_ATTRIBUTE_REQUIRED", + message: "primaryAttribute is required" + }); + } + + try { + const token = await getToken(); + const escapedCaseReference = caseReference.split("'").join("''"); + + const queryUrl = + updateFormCollection + + "?$count=true&$select=_" + + primaryAttribute + + "s_value&$filter=pinswg_name eq '" + + escapedCaseReference + + "'"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "APPEAL_ID_FETCH_FAILED", + message: "Failed to fetch appeal id" }); + } } diff --git a/pages/api/endpoint/getappealpdfdocuments_api.js b/pages/api/endpoint/getappealpdfdocuments_api.js index 7579ed32..6ba1809d 100644 --- a/pages/api/endpoint/getappealpdfdocuments_api.js +++ b/pages/api/endpoint/getappealpdfdocuments_api.js @@ -17,11 +17,11 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -57,45 +57,51 @@ const groupArray = (arr) => { }; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentid; + const incidentID = req.query.incidentid; - if (typeof incidentID === "undefined" || incidentID.length === 0) { - return res.status(400).json(); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); } - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "pinswg_documents?$count=true&$filter=_pinswg_documentids_value eq " + - incidentID + - " and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations"; + const queryUrl = + "pinswg_documents?$count=true&$filter=_pinswg_documentids_value eq " + + incidentID + + " and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations"; - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - // //delete data["pinswg_documentid"]; - // data.value.forEach(function (element) { - // delete element["pinswg_documentid"]; - // }); + ); + // //delete data["pinswg_documentid"]; + // data.value.forEach(function (element) { + // delete element["pinswg_documentid"]; + // }); - // var dataArr = groupArray(data.value); + // var dataArr = groupArray(data.value); - // // var dataStr; - // // _.has(data, "@odata.nextLink") == true && - // // ((dataStr = JSON.stringify(data["@odata.nextLink"])), - // // (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); + // // var dataStr; + // // _.has(data, "@odata.nextLink") == true && + // // ((dataStr = JSON.stringify(data["@odata.nextLink"])), + // // (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - data.value.forEach((item) => { - item.name = item.pinswg_name; - }); - - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + data.value.forEach((item) => { + item.name = item.pinswg_name; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "APPEAL_PDF_DOCUMENTS_FETCH_FAILED", + message: "Failed to fetch appeal PDF documents" + }); + } } diff --git a/pages/api/endpoint/getappealtypes_api.js b/pages/api/endpoint/getappealtypes_api.js index dec3d896..16edd5f8 100644 --- a/pages/api/endpoint/getappealtypes_api.js +++ b/pages/api/endpoint/getappealtypes_api.js @@ -10,42 +10,45 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' " + - (process.env.SHOWSIPS !== "true" - ? "and attributevalue ne 846040002" - : "") + - "&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + const queryUrl = + "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' " + + (process.env.SHOWSIPS !== "true" + ? "and attributevalue ne 846040002" + : "") + + "&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - // for sips - // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + // for sips + // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - //"stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Developments of National Significance' and value ne 'LDP' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + //"stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Developments of National Significance' and value ne 'LDP' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "APPEAL_TYPES_FETCH_FAILED", + message: "Failed to fetch appeal types" }); + } } diff --git a/pages/api/endpoint/getappealtypesfornewappeal_api.js b/pages/api/endpoint/getappealtypesfornewappeal_api.js index 57d9ce8b..06dde026 100644 --- a/pages/api/endpoint/getappealtypesfornewappeal_api.js +++ b/pages/api/endpoint/getappealtypesfornewappeal_api.js @@ -1,3 +1,5 @@ +import { respondSuccess } from "../middleware/apiResponse"; + /** * @swagger * /api/endpoint/getappealtypesfornewappeal_api: @@ -11,8 +13,7 @@ */ const ApiResponse = (req, res) => { - res.statusCode = 200; - res.json({ + return respondSuccess(res, { "value": [ { "value": "Planning Appeal - S78", @@ -20,9 +21,9 @@ const ApiResponse = (req, res) => { "organizationid": "01191a4d-105f-eb11-aaba-00224800be9c", "attributevalue@OData.Community.Display.V1.FormattedValue": "846,040,000", - "attributevalue": 846040000, - }, - ], + "attributevalue": 846040000 + } + ] }); }; diff --git a/pages/api/endpoint/getawaitingsubmission_api.js b/pages/api/endpoint/getawaitingsubmission_api.js index 05339feb..5ea8be59 100644 --- a/pages/api/endpoint/getawaitingsubmission_api.js +++ b/pages/api/endpoint/getawaitingsubmission_api.js @@ -18,39 +18,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + - loggedInUserId + - " and servicestage eq 1 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + + loggedInUserId + + " and servicestage eq 1 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + data.value.forEach(function (element) { + element.pinswg_title = element.title; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "AWAITING_SUBMISSION_FETCH_FAILED", + message: "Failed to fetch awaiting submissions" + }); + } } diff --git a/pages/api/endpoint/getbasicdnssearch_api.js b/pages/api/endpoint/getbasicdnssearch_api.js index 82a18e29..68c29b9e 100644 --- a/pages/api/endpoint/getbasicdnssearch_api.js +++ b/pages/api/endpoint/getbasicdnssearch_api.js @@ -17,37 +17,41 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002)and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002)and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_DNS_SEARCH_FETCH_FAILED", + message: "Failed to fetch basic DNS search results" }); + } } diff --git a/pages/api/endpoint/getbasicdnssearchdetails_api.js b/pages/api/endpoint/getbasicdnssearchdetails_api.js index 7bd99f7c..1ac1acf3 100644 --- a/pages/api/endpoint/getbasicdnssearchdetails_api.js +++ b/pages/api/endpoint/getbasicdnssearchdetails_api.js @@ -11,45 +11,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; +import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var caseReference = req.query.caseReference; - var token = await getToken(); + const caseReference = req.query.caseReference; - var queryUrl = - "pinswg_dnses?$filter=pinswg_name eq '" + - caseReference + - "&$count=true"; - //" and statuscode eq 1&$count=true"; + if ( + typeof caseReference !== "string" || + caseReference.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CASE_REFERENCE_REQUIRED", + message: "caseReference is required" + }); + } - queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); + try { + const token = await getToken(); - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : typeof caseReference != "undefined" && caseReference.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); + let queryUrl = + "pinswg_dnses?$filter=pinswg_name eq '" + + caseReference + + "&$count=true"; - return apiResponse; + queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); + + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeaders(token.access_token) + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_DNS_SEARCH_DETAILS_FETCH_FAILED", + message: "Failed to fetch basic DNS search details" + }); + } } diff --git a/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js b/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js index fd87438b..d85e1668 100644 --- a/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js +++ b/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js @@ -10,50 +10,59 @@ * description: Success */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var caseReference = req.query.caseReference; - var token = await getToken(); + const caseReference = req.query.caseReference; - var queryUrl = - "pinswg_dnses?$filter=pinswg_name eq '" + - caseReference + - "&$count=true"; - //" and statuscode eq 1&$count=true"; + if ( + typeof caseReference !== "string" || + caseReference.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CASE_REFERENCE_REQUIRED", + message: "caseReference is required" + }); + } - queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); + try { + const token = await getToken(); - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : typeof caseReference != "undefined" && caseReference.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - return res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); + let queryUrl = + "pinswg_dnses?$filter=pinswg_name eq '" + + caseReference + + "&$count=true"; - return apiResponse; + queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); + + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPaged(token.access_token) + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_DNS_SEARCH_DETAILS_PAGED_FETCH_FAILED", + message: "Failed to fetch paged basic DNS search details" + }); + } } diff --git a/pages/api/endpoint/getbasicdnssearchpaged_api.js b/pages/api/endpoint/getbasicdnssearchpaged_api.js index dceb0c23..4d8d7791 100644 --- a/pages/api/endpoint/getbasicdnssearchpaged_api.js +++ b/pages/api/endpoint/getbasicdnssearchpaged_api.js @@ -48,43 +48,75 @@ import { azureHeadersPagedCustom } from "../../../actions/core/headers"; import { getToken } from "../../../actions/core/token"; import { consoleLogger } from "../../../actions/core/logger"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var pageNumber = req.query.pageNumber; - var token = await getToken(); + const pageNumber = req.query.pageNumber; + const orderby = req.query.orderby; + const fieldSort = req.query.fieldSort; + const showNumberOfRecords = req.query.showNumberOfRecords; - var orderby = req.query.orderby; - var fieldSort = req.query.fieldSort; - var showNumberOfRecords = req.query.showNumberOfRecords; + if (typeof orderby !== "string" || orderby.trim().length === 0) { + return respondError(res, { + status: 400, + code: "ORDER_BY_REQUIRED", + message: "orderby is required" + }); + } - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + '' - : ""); + if (typeof fieldSort !== "string" || fieldSort.trim().length === 0) { + return respondError(res, { + status: 400, + code: "FIELD_SORT_REQUIRED", + message: "fieldSort is required" + }); + } - return axios - .get( + if ( + typeof showNumberOfRecords !== "string" || + showNumberOfRecords.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "SHOW_NUMBER_OF_RECORDS_REQUIRED", + message: "showNumberOfRecords is required" + }); + } + + try { + const token = await getToken(); + + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + '' + : ""); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - return res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_DNS_SEARCH_PAGED_FETCH_FAILED", + message: "Failed to fetch paged basic DNS search results" }); + } } diff --git a/pages/api/endpoint/getbasicdnsurlsearch_api.js b/pages/api/endpoint/getbasicdnsurlsearch_api.js index b4286ceb..c1a2f175 100644 --- a/pages/api/endpoint/getbasicdnsurlsearch_api.js +++ b/pages/api/endpoint/getbasicdnsurlsearch_api.js @@ -17,55 +17,56 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchString; + const searchString = req.query.searchString; - if (!searchString || String(searchString).trim().length === 0) { - return res.status(400).json(); + if (typeof searchString !== "string" || searchString.trim().length === 0) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchString is required" + }); } - var token = await getToken(); + try { + const token = await getToken(); + const escapedSearchString = searchString.replace(/\'/g, "''"); - searchString = searchString.replace(/\'/g, "''"); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + escapedSearchString + + "') or contains(ticketnumber, '" + + escapedSearchString + + "')) and (pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002 ) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - searchString + - "') or contains(ticketnumber, '" + - searchString + - "')) and (pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002 ) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPaged(token.access_token) + ); - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : searchString.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - return apiResponse; + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_DNS_URL_SEARCH_FETCH_FAILED", + message: "Failed to fetch DNS URL search results" + }); + } } diff --git a/pages/api/endpoint/getbasicpartsaveddetails_api.js b/pages/api/endpoint/getbasicpartsaveddetails_api.js index 537a76d9..47ced874 100644 --- a/pages/api/endpoint/getbasicpartsaveddetails_api.js +++ b/pages/api/endpoint/getbasicpartsaveddetails_api.js @@ -1,41 +1,72 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeadersNoOdata } from "../../../actions/core/headers"; +import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var appealTypeName = req.query.appealTypeName; - var primaryIdAttribute = req.query.primaryIdAttribute; - var incidentID = req.query.incidentID; - var token = await getToken(); + const appealTypeName = req.query.appealTypeName; + const primaryIdAttribute = req.query.primaryIdAttribute; + const incidentID = req.query.incidentID; - var queryUrl = - appealTypeName + - "?$filter=_" + - primaryIdAttribute + - "s_value eq " + - incidentID + - "&$count=true"; + if ( + typeof appealTypeName !== "string" || + appealTypeName.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_NAME_REQUIRED", + message: "appealTypeName is required" + }); + } - queryUrl = queryUrl; //+ getSelectQuery(appealTypeName); + if ( + typeof primaryIdAttribute !== "string" || + primaryIdAttribute.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "PRIMARY_ID_ATTRIBUTE_REQUIRED", + message: "primaryIdAttribute is required" + }); + } - //console.log("///////////\nquery: ", queryUrl, "<<< { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_PART_SAVED_DETAILS_FETCH_FAILED", + message: "Failed to fetch basic part-saved details" }); + } } diff --git a/pages/api/endpoint/getbasicsearch_by_address_api.js b/pages/api/endpoint/getbasicsearch_by_address_api.js index c5bf4477..02ab6644 100644 --- a/pages/api/endpoint/getbasicsearch_by_address_api.js +++ b/pages/api/endpoint/getbasicsearch_by_address_api.js @@ -4,6 +4,7 @@ import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -728,15 +729,16 @@ export default async function ApiProxy(req, res) { (record) => record.pinswg_publishtoweb ); - return res.status(200).json({ + return respondSuccess(res, { "@odata.count": searchResultsObj.length, value: searchResultsObj }); } catch (error) { consoleLogger(error); - return res.status(500).json({ - message: "Failed to retrieve search results.", - error: error?.message || error + return respondError(res, { + status: 500, + code: "BASIC_SEARCH_BY_ADDRESS_FETCH_FAILED", + message: "Failed to retrieve search results" }); } } diff --git a/pages/api/endpoint/getbasicsearch_by_lparref_api.js b/pages/api/endpoint/getbasicsearch_by_lparref_api.js index f50f2db8..228bf990 100644 --- a/pages/api/endpoint/getbasicsearch_by_lparref_api.js +++ b/pages/api/endpoint/getbasicsearch_by_lparref_api.js @@ -1,9 +1,9 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -285,9 +285,11 @@ export default async function ApiProxy(req, res) { let lpaRef = (req.query.lpaRef || "").trim(); if (!lpaRef) { - return res - .status(400) - .json({ error: "Missing required query param: lpaRef" }); + return respondError(res, { + status: 400, + code: "LPA_REF_REQUIRED", + message: "lpaRef is required" + }); } // ✅ Escape single quotes for OData @@ -298,10 +300,6 @@ export default async function ApiProxy(req, res) { // contains(tolower(pinswg_lpaapplicationreference), 'lowered search') const lowered = lpaRef.toLowerCase(); - console.log( - `\n=== LPA REF SEARCH ===\nSearching for "${lpaRef}" (case-insensitive contains)\nAcross ${appealTypeArrayWithLpaRef.length} entity types\n======================\n` - ); - const resultsPerType = await Promise.all( appealTypeArrayWithLpaRef.map(async (item) => { // You can expand/select more fields if needed, but keep light for speed @@ -323,17 +321,6 @@ export default async function ApiProxy(req, res) { `&$count=true` + `&$orderby=modifiedon desc`; - console.log( - "\n------------------------------------------\n", - "Entity:", - item.LogicalCollectionName, - "\nQuery:", - queryUrl, - "\nFull:", - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - "\n------------------------------------------\n" - ); - const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) @@ -353,10 +340,6 @@ export default async function ApiProxy(req, res) { ] || null })); - console.log( - `Returned ${normalized.length} records from ${item.LogicalCollectionName}` - ); - return normalized; }) ); @@ -372,9 +355,13 @@ export default async function ApiProxy(req, res) { value: flattened }; - return res.status(200).json(output); + return respondSuccess(res, output); } catch (error) { consoleLogger(error); - return res.status(400).json(error); + return respondError(res, { + status: 400, + code: "BASIC_SEARCH_LPA_REF_FETCH_FAILED", + message: "Failed to fetch basic search by lpa reference" + }); } } diff --git a/pages/api/endpoint/getbasicsearchdetails_api.js b/pages/api/endpoint/getbasicsearchdetails_api.js index 7370f714..1224ace0 100644 --- a/pages/api/endpoint/getbasicsearchdetails_api.js +++ b/pages/api/endpoint/getbasicsearchdetails_api.js @@ -30,80 +30,114 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { getNavigationPropertyByPrimaryAttribute } from "../../../components/utils"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var appealTypeName = req.query.appealTypeName; - var primaryIdAttribute = req.query.primaryIdAttribute; - var incidentID = req.query.incidentID; - var token = await getToken(); + const appealTypeName = req.query.appealTypeName; + const primaryIdAttribute = req.query.primaryIdAttribute; + const incidentID = req.query.incidentID; - let navigationProperty = - getNavigationPropertyByPrimaryAttribute( - primaryIdAttribute - ).NavigationProperty; + if ( + typeof appealTypeName !== "string" || + appealTypeName.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_NAME_REQUIRED", + message: "appealTypeName is required" + }); + } - var queryUrl = - appealTypeName + - "?$filter=_" + - (primaryIdAttribute == "pinswg_sipscase" - ? "pinswg_sipscase_value" - : primaryIdAttribute + "s_value ") + - " eq " + - incidentID + - "&$count=true" + - "&$expand=" + - navigationProperty + - "($select=ticketnumber)"; + if ( + typeof primaryIdAttribute !== "string" || + primaryIdAttribute.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "PRIMARY_ID_ATTRIBUTE_REQUIRED", + message: "primaryIdAttribute is required" + }); + } - //" and statuscode eq 1&$count=true"; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" + }); + } - // queryUrl = - // queryUrl + - // getSelectQuery(appealTypeName) + - // ",_" + - // (primaryIdAttribute == "pinswg_sipscase" - // ? "pinswg_sipscase_value" - // : primaryIdAttribute + "s_value"); + try { + const token = await getToken(); - queryUrl = queryUrl + getSelectQuery(appealTypeName); + const navigationProperty = + getNavigationPropertyByPrimaryAttribute( + primaryIdAttribute + ).NavigationProperty; - // console.log( - // "\n==========================================\n", - // "\nSearch Details query ", - // "\nAppealType: " + appealTypeName, - // "\nIncident ID: " + incidentID, - // "\n\nQuery url: " + queryUrl, - // "\n==========================================\n" - // ); + let queryUrl = + appealTypeName + + "?$filter=_" + + (primaryIdAttribute == "pinswg_sipscase" + ? "pinswg_sipscase_value" + : primaryIdAttribute + "s_value ") + + " eq " + + incidentID + + "&$count=true" + + "&$expand=" + + navigationProperty + + "($select=ticketnumber)"; - return axios - .get( + //" and statuscode eq 1&$count=true"; + + // queryUrl = + // queryUrl + + // getSelectQuery(appealTypeName) + + // ",_" + + // (primaryIdAttribute == "pinswg_sipscase" + // ? "pinswg_sipscase_value" + // : primaryIdAttribute + "s_value"); + + queryUrl = queryUrl + getSelectQuery(appealTypeName); + + // console.log( + // "\n==========================================\n", + // "\nSearch Details query ", + // "\nAppealType: " + appealTypeName, + // "\nIncident ID: " + incidentID, + // "\n\nQuery url: " + queryUrl, + // "\n==========================================\n" + // ); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - let flattened = data.value.map((r) => ({ - ...r, - ticketnumber: r[navigationProperty]?.ticketnumber || null - })); + ); - data.value = flattened; + let flattened = data.value.map((r) => ({ + ...r, + ticketnumber: r[navigationProperty]?.ticketnumber || null + })); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + data.value = flattened; + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_SEARCH_DETAILS_FETCH_FAILED", + message: "Failed to fetch basic search details" }); + } } diff --git a/pages/api/endpoint/getbasicsearchdetailspaged_api.js b/pages/api/endpoint/getbasicsearchdetailspaged_api.js index e12924db..ba20d7a7 100644 --- a/pages/api/endpoint/getbasicsearchdetailspaged_api.js +++ b/pages/api/endpoint/getbasicsearchdetailspaged_api.js @@ -30,7 +30,6 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; @@ -38,82 +37,105 @@ import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { getNavigationPropertyByPrimaryAttribute } from "../../../components/utils"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var appealTypeName = req.query.appealTypeName; - var primaryIdAttribute = req.query.primaryIdAttribute; - var incidentID = req.query.incidentID; + const appealTypeName = req.query.appealTypeName; + const primaryIdAttribute = req.query.primaryIdAttribute; + const incidentID = req.query.incidentID; if ( - !appealTypeName || - !primaryIdAttribute || - !incidentID || - String(appealTypeName).trim().length === 0 || - String(primaryIdAttribute).trim().length === 0 || - String(incidentID).trim().length === 0 + typeof appealTypeName !== "string" || + appealTypeName.trim().length === 0 ) { - return res.status(400).json(); + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_NAME_REQUIRED", + message: "appealTypeName is required" + }); } - var token = await getToken(); + if ( + typeof primaryIdAttribute !== "string" || + primaryIdAttribute.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "PRIMARY_ID_ATTRIBUTE_REQUIRED", + message: "primaryIdAttribute is required" + }); + } - let navigationProperty = - getNavigationPropertyByPrimaryAttribute( - primaryIdAttribute - ).NavigationProperty; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" + }); + } - // "?$filter=_" + - // (primaryIdAttribute == "pinswg_sipscase" - // ? "pinswg_sipscase_value" - // : primaryIdAttribute + "s_value ") + - // " eq " + - // incidentID + + try { + const token = await getToken(); - var queryUrl = - appealTypeName + - "?$filter=" + - incidentID + - "&$count=true" + - "&$expand=" + - navigationProperty + - "($select=ticketnumber)"; + const navigationProperty = + getNavigationPropertyByPrimaryAttribute( + primaryIdAttribute + ).NavigationProperty; - //" and statuscode eq 1&$count=true"; + // "?$filter=_" + + // (primaryIdAttribute == "pinswg_sipscase" + // ? "pinswg_sipscase_value" + // : primaryIdAttribute + "s_value ") + + // " eq " + + // incidentID + - queryUrl = - queryUrl + - getSelectQuery(appealTypeName) + - ",_" + - (primaryIdAttribute == "pinswg_sipscase" - ? "pinswg_sipscase_value" - : primaryIdAttribute + "s_value"); + let queryUrl = + appealTypeName + + "?$filter=" + + incidentID + + "&$count=true" + + "&$expand=" + + navigationProperty + + "($select=ticketnumber)"; - return axios - .get( + //" and statuscode eq 1&$count=true"; + + queryUrl = + queryUrl + + getSelectQuery(appealTypeName) + + ",_" + + (primaryIdAttribute == "pinswg_sipscase" + ? "pinswg_sipscase_value" + : primaryIdAttribute + "s_value"); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - let flattened = data.value.map((r) => ({ - ...r, - ticketnumber: r[navigationProperty]?.ticketnumber || null - })); + ); - data.value = flattened; + let flattened = data.value.map((r) => ({ + ...r, + ticketnumber: r[navigationProperty]?.ticketnumber || null + })); - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); + data.value = flattened; - return res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_SEARCH_DETAILS_PAGED_FETCH_FAILED", + message: "Failed to fetch paged basic search details" }); + } } diff --git a/pages/api/endpoint/getbasicsearchpaged_api.js b/pages/api/endpoint/getbasicsearchpaged_api.js index 547f5846..12bd6d36 100644 --- a/pages/api/endpoint/getbasicsearchpaged_api.js +++ b/pages/api/endpoint/getbasicsearchpaged_api.js @@ -48,70 +48,96 @@ import { azureHeadersPagedCustom } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchString; - var pageNumber = req.query.pageNumber; + let searchString = req.query.searchString; + const pageNumber = req.query.pageNumber; + const orderby = req.query.orderby; + const fieldSort = req.query.fieldSort; + const showNumberOfRecords = req.query.showNumberOfRecords; - if (typeof searchString === "undefined" || searchString.length === 0) { - return res.status(400).json(); + if (typeof searchString !== "string" || searchString.trim().length === 0) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchString is required" + }); } - var token = await getToken(); + if (typeof orderby !== "string" || orderby.trim().length === 0) { + return respondError(res, { + status: 400, + code: "ORDER_BY_REQUIRED", + message: "orderby is required" + }); + } - var orderby = req.query.orderby; - var fieldSort = req.query.fieldSort; - var showNumberOfRecords = req.query.showNumberOfRecords; + if (typeof fieldSort !== "string" || fieldSort.trim().length === 0) { + return respondError(res, { + status: 400, + code: "FIELD_SORT_REQUIRED", + message: "fieldSort is required" + }); + } - searchString = searchString.replace(/\'/g, "''"); + if ( + typeof showNumberOfRecords !== "string" || + showNumberOfRecords.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "SHOW_NUMBER_OF_RECORDS_REQUIRED", + message: "showNumberOfRecords is required" + }); + } - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - searchString + - "') or contains(ticketnumber, '" + - searchString + - "') or contains(pinswg_lpareference, '" + - searchString + - "')) and pinswg_appealcasetype ne null " + - (process.env.SHOWSIPS !== "true" - ? "and pinswg_appealcasetype ne 846040002 " - : "") + - "and pinswg_publishtoweb eq true&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + '' - : ""); + try { + const token = await getToken(); - var apiResponse = - searchString.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom( - token.access_token, - showNumberOfRecords - ) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = - dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); + searchString = searchString.replace(/\'/g, "''"); - return apiResponse; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + searchString + + "') or contains(ticketnumber, '" + + searchString + + "') or contains(pinswg_lpareference, '" + + searchString + + "')) and pinswg_appealcasetype ne null " + + (process.env.SHOWSIPS !== "true" + ? "and pinswg_appealcasetype ne 846040002 " + : "") + + "and pinswg_publishtoweb eq true&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + '' + : ""); + + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPagedCustom(token.access_token, showNumberOfRecords) + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "BASIC_SEARCH_PAGED_FETCH_FAILED", + message: "Failed to fetch paged basic search results" + }); + } } diff --git a/pages/api/endpoint/getcase_api.js b/pages/api/endpoint/getcase_api.js index 8a11cd42..6202e4d5 100644 --- a/pages/api/endpoint/getcase_api.js +++ b/pages/api/endpoint/getcase_api.js @@ -11,41 +11,52 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentID; - var token = await getToken(); - var queryUrl = - "incidents(" + - incidentID + - ")?$select=ticketnumber,title,pinswg_appealcasetype"; + const incidentID = req.query.incidentID; - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + const queryUrl = + "incidents(" + + incidentID + + ")?$select=ticketnumber,title,pinswg_appealcasetype"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_FETCH_FAILED", + message: "Failed to fetch case" }); + } } diff --git a/pages/api/endpoint/getcasebyid_api.js b/pages/api/endpoint/getcasebyid_api.js index 7f7f0392..03783f2d 100644 --- a/pages/api/endpoint/getcasebyid_api.js +++ b/pages/api/endpoint/getcasebyid_api.js @@ -11,38 +11,49 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentID; - var token = await getToken(); - var queryUrl = "incidents(" + incidentID + ")"; + const incidentID = req.query.incidentID; - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + const queryUrl = "incidents(" + incidentID + ")"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json([data]); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, [data]); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_BY_ID_FETCH_FAILED", + message: "Failed to fetch case by id" }); + } } diff --git a/pages/api/endpoint/getdnscoords_api.js b/pages/api/endpoint/getdnscoords_api.js index b63bf64c..4ecbdc19 100644 --- a/pages/api/endpoint/getdnscoords_api.js +++ b/pages/api/endpoint/getdnscoords_api.js @@ -1,10 +1,10 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import OSPoint from "ospoint"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -75,16 +75,16 @@ const keysToRemove = [ ]; export default async function ApiProxy(req, res) { - var token = await getToken(); + const token = await getToken(); - var queryUrl = + const queryUrl = "pinswg_dnses?$select=pinswg_projectlocation,pinswg_mapzoomlevel,pinswg_name,pinswg_anticipatedgridreferenceeastingtext,pinswg_anticipatedgridreferencenorthingtext,pinswg_projectname,pinswg_dnsid,_pinswg_associatedlpa_value,_pinswg_appellant_value&$filter=pinswg_anticipatedgridreferencenorthingtext ne null and pinswg_anticipatedgridreferenceeastingtext ne null&$count=true"; //console.log("dns ", queryUrl); - var queryUrlSips = + const queryUrlSips = "pinswg_sipses?$select=pinswg_projectlocation,pinswg_mapzoomlevel,pinswg_name,pinswg_anticipatedgridrefeasting,pinswg_anticipatedgridrefnorthing,pinswg_projectname,pinswg_sipscase,_pinswg_associatedlpa_value,_pinswg_appellant_value&$filter=pinswg_anticipatedgridrefeasting ne null and pinswg_anticipatedgridrefnorthing ne null&$count=true"; - var coordsObj = { value: [] }; + const coordsObj = { value: [] }; try { // First Axios request @@ -150,11 +150,15 @@ export default async function ApiProxy(req, res) { }; return req.query.hasOwnProperty("fordmw") - ? res.status(200).json(updatedData) - : res.status(200).json(coordsObj); + ? respondSuccess(res, updatedData) + : respondSuccess(res, coordsObj); } catch (error) { // Catch any errors and send an error response consoleLogger(error); - return res.status(400).json(error); + return respondError(res, { + status: 400, + code: "DNS_COORDS_FETCH_FAILED", + message: "Failed to fetch DNS coordinates" + }); } } diff --git a/pages/api/endpoint/getdnslist_api.js b/pages/api/endpoint/getdnslist_api.js index 2b555deb..14251a39 100644 --- a/pages/api/endpoint/getdnslist_api.js +++ b/pages/api/endpoint/getdnslist_api.js @@ -1,44 +1,40 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_appealcasetype,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,statuscode,ticketnumber,title &$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_appealcasetype,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,statuscode,ticketnumber,title &$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - function renameJsonKey(jsonObj, oldKey, newKey) { - if (jsonObj.hasOwnProperty(oldKey)) { - jsonObj[newKey] = jsonObj[oldKey]; // Add new key with the same value - delete jsonObj[oldKey]; // Delete old key - } - return jsonObj; - } - - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - var dataStr; + ); - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + let dataStr; + + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "DNS_LIST_FETCH_FAILED", + message: "Failed to fetch DNS list" }); + } } diff --git a/pages/api/endpoint/getformdata_api.js b/pages/api/endpoint/getformdata_api.js index acea3b3c..39ac3034 100644 --- a/pages/api/endpoint/getformdata_api.js +++ b/pages/api/endpoint/getformdata_api.js @@ -1,33 +1,45 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var whichForm = req.query.whichForm; - var token = await getToken(); + const whichForm = req.query.whichForm; - var queryUrl = - "systemforms?$select=formid,name,formxml,type,objecttypecode&$filter=(objecttypecode eq 'pinswg_" + - whichForm + - "' and type eq 2)&$count=true&$top=201"; + if (typeof whichForm !== "string" || whichForm.trim().length === 0) { + return respondError(res, { + status: 400, + code: "WHICH_FORM_REQUIRED", + message: "whichForm is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "systemforms?$select=formid,name,formxml,type,objecttypecode&$filter=(objecttypecode eq 'pinswg_" + + whichForm + + "' and type eq 2)&$count=true&$top=201"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "FORM_DATA_FETCH_FAILED", + message: "Failed to fetch form data" }); + } } diff --git a/pages/api/endpoint/gethash_api.js b/pages/api/endpoint/gethash_api.js index 1524bce0..f0ef869e 100644 --- a/pages/api/endpoint/gethash_api.js +++ b/pages/api/endpoint/gethash_api.js @@ -1,6 +1,7 @@ import { hashAPIPath } from "../../../actions/core/hash"; import nextConnect from "next-connect"; import { getSession } from "next-auth/react"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const ApiProxy = nextConnect(); @@ -8,7 +9,11 @@ ApiProxy.get(async (req, res) => { const session = await getSession({ req }); if (!session) { - return res.status(401).json(); + return respondError(res, { + status: 401, + code: "UNAUTHENTICATED", + message: "Authentication required" + }); } const rawQueryPath = req.query.path; @@ -39,10 +44,14 @@ ApiProxy.get(async (req, res) => { !allowedPrefix.some((prefix) => queryPath.startsWith(prefix)) || queryPath.includes("/api/endpoint/gethash_api") ) { - return res.status(400).json(); + return respondError(res, { + status: 400, + code: "INVALID_HASH_PATH", + message: "Invalid path for hash generation" + }); } - return res.status(200).json({ hash: hashAPIPath(rawQueryPath) }); + return respondSuccess(res, { hash: hashAPIPath(rawQueryPath) }); }); export default ApiProxy; diff --git a/pages/api/endpoint/getincidentbyid_api.js b/pages/api/endpoint/getincidentbyid_api.js index 15b135b2..591d972c 100644 --- a/pages/api/endpoint/getincidentbyid_api.js +++ b/pages/api/endpoint/getincidentbyid_api.js @@ -16,53 +16,49 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; -import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchString; - var token = await getToken(); + let searchString = req.query.searchString; - searchString = searchString.replace(/\'/g, "''"); + if (typeof searchString !== "string" || searchString.trim().length === 0) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchString is required" + }); + } - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=incidentid eq " + - searchString + - " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; + try { + const token = await getToken(); - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : searchString.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ) - .then(({ data }) => { - //console.log( - ("\n==========================================\n", - // "basic search incident: " + queryUrl, - // "\n\n", - // "data: " + JSON.stringify(data), - // "\n\n", - "publihs:" + JSON.stringify(data.value[0]), - "\n==========================================\n"); - //); - return res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); + searchString = searchString.replace(/\'/g, "''"); - return apiResponse; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=incidentid eq " + + searchString + + " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; + + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeaders(token.access_token) + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "INCIDENT_BY_ID_FETCH_FAILED", + message: "Failed to fetch incident by id" + }); + } } diff --git a/pages/api/endpoint/getispublishedbyid_api.js b/pages/api/endpoint/getispublishedbyid_api.js index 00318960..f434dac0 100644 --- a/pages/api/endpoint/getispublishedbyid_api.js +++ b/pages/api/endpoint/getispublishedbyid_api.js @@ -16,48 +16,49 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchString; - var token = await getToken(); + const searchString = req.query.searchString; - searchString = searchString.replace(/\'/g, "''"); + if (typeof searchString !== "string" || searchString.trim().length === 0) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchString is required" + }); + } - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,ticketnumber,title, _primarycontactid_value&$filter=incidentid eq " + - searchString + - " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; + try { + const token = await getToken(); - console.log( - "\n==========================================\n", - "basic search: " + queryUrl, - "\n\n", - "basic search relay link: " + - WEBAPI_URL + - queryUrl + - hashAPIPath(queryUrl), - "\n==========================================\n" - ); + const escapedSearchString = searchString.split("'").join("''"); - return axios - .get( + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,ticketnumber,title, _primarycontactid_value&$filter=incidentid eq " + + escapedSearchString + + " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "IS_PUBLISHED_FETCH_FAILED", + message: "Failed to fetch published state" }); + } } diff --git a/pages/api/endpoint/getlinkedcases_api.js b/pages/api/endpoint/getlinkedcases_api.js index b08b0823..d4b9ed84 100644 --- a/pages/api/endpoint/getlinkedcases_api.js +++ b/pages/api/endpoint/getlinkedcases_api.js @@ -17,35 +17,50 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var parentIncidentid = req.query.parentincidentid; - var token = await getToken(); + const parentIncidentid = req.query.parentincidentid; - var queryUrl = - "incidents?$count=true&$filter=_parentcaseid_value eq " + - parentIncidentid + - " and pinswg_appealcasetype ne null and pinswg_publishtoweb eq true &$select=title, incidentid"; + if ( + typeof parentIncidentid !== "string" || + parentIncidentid.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "PARENT_INCIDENT_ID_REQUIRED", + message: "parentincidentid is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "incidents?$count=true&$filter=_parentcaseid_value eq " + + parentIncidentid + + " and pinswg_appealcasetype ne null and pinswg_publishtoweb eq true &$select=title, incidentid"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "LINKED_CASES_FETCH_FAILED", + message: "Failed to fetch linked cases" }); + } } diff --git a/pages/api/endpoint/getlpa_api.js b/pages/api/endpoint/getlpa_api.js index 33cb9409..51a75b87 100644 --- a/pages/api/endpoint/getlpa_api.js +++ b/pages/api/endpoint/getlpa_api.js @@ -13,33 +13,35 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "accounts?$count=true&$filter=pinswg_isalocalplanningauthorityaccount eq 846040000&$select=name&$orderby=name asc"; + const queryUrl = + "accounts?$count=true&$filter=pinswg_isalocalplanningauthorityaccount eq 846040000&$select=name&$orderby=name asc"; - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "LPA_FETCH_FAILED", + message: "Failed to fetch local planning authorities" }); + } } diff --git a/pages/api/endpoint/getmandatoryfields_api.js b/pages/api/endpoint/getmandatoryfields_api.js index 756e21ad..8fb1a5bf 100644 --- a/pages/api/endpoint/getmandatoryfields_api.js +++ b/pages/api/endpoint/getmandatoryfields_api.js @@ -1,3 +1,5 @@ +import { respondError, respondSuccess } from "../middleware/apiResponse"; + const WORDKEY = process.env.HASHKEY; const WEBAPI_URL = @@ -5,16 +7,32 @@ const WEBAPI_URL = "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var whichForm = req.query.whichForm; + const whichForm = req.query.whichForm; + + if (typeof whichForm !== "string" || whichForm.trim().length === 0) { + return respondError(res, { + status: 400, + code: "WHICH_FORM_REQUIRED", + message: "whichForm is required" + }); + } // var queryUrl = // "EntityDefinitions(LogicalName='pinswg_" + // whichForm + // "')/Attributes?$count=true&$select=LogicalName,RequiredLevel"; - const mandatoryFieldsData = require("../../../data/mandatoryfields/pinswg_" + - whichForm + - ".json"); + try { + const mandatoryFieldsData = require( + "../../../data/mandatoryfields/pinswg_" + whichForm + ".json" + ); - return res.status(200).json(mandatoryFieldsData); + return respondSuccess(res, mandatoryFieldsData); + } catch (error) { + return respondError(res, { + status: 404, + code: "MANDATORY_FIELDS_NOT_FOUND", + message: "Mandatory fields data not found for requested form" + }); + } } diff --git a/pages/api/endpoint/getmycases_api.js b/pages/api/endpoint/getmycases_api.js index d510c6f7..5420fde2 100644 --- a/pages/api/endpoint/getmycases_api.js +++ b/pages/api/endpoint/getmycases_api.js @@ -18,43 +18,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,pinswg_publishtoweb,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + - loggedInUserId + - " and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - // Removed servicestage but not sure why..... - //" and servicestage eq 0 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + try { + const token = await getToken(); - //console.log("///////////\nPortal query: ", queryUrl, "<<< { - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + data.value.forEach(function (element) { + element.pinswg_title = element.title; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "MY_CASES_FETCH_FAILED", + message: "Failed to fetch my cases" + }); + } } diff --git a/pages/api/endpoint/getmylpacases_api.js b/pages/api/endpoint/getmylpacases_api.js index d39dc467..47daab59 100644 --- a/pages/api/endpoint/getmylpacases_api.js +++ b/pages/api/endpoint/getmylpacases_api.js @@ -24,51 +24,72 @@ import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var lpaid = req.query.lpaid; - var token = await getToken(); + const lpaid = req.query.lpaid; - const lpaList = await getLPA(); + if (typeof lpaid !== "string" || lpaid.trim().length === 0) { + return respondError(res, { + status: 400, + code: "LPA_ID_REQUIRED", + message: "lpaid is required" + }); + } - const lpaGUID = jsonpath({ - path: '$..[?(@ && @.name=="' + lpaid + '")]', - json: lpaList, - eval: true - }); + try { + const token = await getToken(); + const lpaList = await getLPA(); - //console.log( - // '$..[?(@ && @.name="' + lpaid + '")]', - // lpaList, - // lpaid, - // "LPAGUID=", - // lpaGUID - // ); + const lpaGUID = jsonpath({ + path: '$..[?(@ && @.name=="' + lpaid + '")]', + json: lpaList, + eval: true + }); - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_pinswg_associatedlpa_value eq " + - lpaGUID[0].accountid + - " and servicestage eq 0 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + if (!Array.isArray(lpaGUID) || !lpaGUID[0]?.accountid) { + return respondError(res, { + status: 404, + code: "LPA_NOT_FOUND", + message: "No matching LPA found" + }); + } - //console.log("///////////\nPortal query: ", queryUrl, "<<< { - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + data.value.forEach(function (element) { + element.pinswg_title = element.title; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "MY_LPA_CASES_FETCH_FAILED", + message: "Failed to fetch my LPA cases" + }); + } } diff --git a/pages/api/endpoint/getmyrepresentations_api.js b/pages/api/endpoint/getmyrepresentations_api.js index 0fc8d7a8..9ad04a89 100644 --- a/pages/api/endpoint/getmyrepresentations_api.js +++ b/pages/api/endpoint/getmyrepresentations_api.js @@ -18,37 +18,50 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var queryUrl = - "pinswg_representationses?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$count=true&$orderby=createdon desc"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + try { + const token = await getToken(); - return axios - .get( + const queryUrl = + "pinswg_representationses?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$count=true&$orderby=createdon desc"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "MY_REPRESENTATIONS_FETCH_FAILED", + message: "Failed to fetch my representations" }); + } } diff --git a/pages/api/endpoint/getpartsavedappeal_api.js b/pages/api/endpoint/getpartsavedappeal_api.js index 1d9bc2de..ad1358c5 100644 --- a/pages/api/endpoint/getpartsavedappeal_api.js +++ b/pages/api/endpoint/getpartsavedappeal_api.js @@ -1,55 +1,54 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var searchString = req.query.searchString; - var token = await getToken(); + const searchString = req.query.searchString; - searchString = searchString.replace(/\'/g, "''"); + if (typeof searchString !== "string" || searchString.trim().length === 0) { + return respondError(res, { + status: 400, + code: "SEARCH_STRING_REQUIRED", + message: "searchString is required" + }); + } - var queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - searchString + - "') or contains(ticketnumber, '" + - searchString + - "')) and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + try { + const token = await getToken(); + const escapedSearchString = searchString.replace(/\'/g, "''"); - //console.log("basic search ", queryUrl); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + escapedSearchString + + "') or contains(ticketnumber, '" + + escapedSearchString + + "')) and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : searchString.length > 0 - ? axios - .get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - var dataStr; - // dataStr = dataStr.replace(/: true/gm, `: "Yes"`); - // dataStr = dataStr.replace(/: false/gm, `: "No"`); + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPaged(token.access_token) + ); - // console.log("replaced strinig:", dataStr); + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }) - : res.status(400).json(); - - return apiResponse; + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "PART_SAVED_APPEAL_FETCH_FAILED", + message: "Failed to fetch part-saved appeals" + }); + } } diff --git a/pages/api/endpoint/getpicklists_api.js b/pages/api/endpoint/getpicklists_api.js index eb293cf2..e69851c8 100644 --- a/pages/api/endpoint/getpicklists_api.js +++ b/pages/api/endpoint/getpicklists_api.js @@ -17,6 +17,8 @@ * description: hello world */ +import { respondError, respondSuccess } from "../middleware/apiResponse"; + const WORDKEY = process.env.HASHKEY; const WEBAPI_URL = @@ -24,16 +26,32 @@ const WEBAPI_URL = "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var whichForm = req.query.whichForm; + const whichForm = req.query.whichForm; + + if (typeof whichForm !== "string" || whichForm.trim().length === 0) { + return respondError(res, { + status: 400, + code: "WHICH_FORM_REQUIRED", + message: "whichForm is required" + }); + } // var queryUrl = // "EntityDefinitions(LogicalName='pinswg_" + // whichForm + // "')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$select=LogicalName&$expand=OptionSet,GlobalOptionSet&$count=true"; - const pickListData = require("../../../data/picklistdata/pinswg_" + - whichForm + - ".json"); + try { + const pickListData = require( + "../../../data/picklistdata/pinswg_" + whichForm + ".json" + ); - return res.status(200).json(pickListData); + return respondSuccess(res, pickListData); + } catch (error) { + return respondError(res, { + status: 404, + code: "PICKLISTS_NOT_FOUND", + message: "Picklist data not found for requested form" + }); + } } diff --git a/pages/api/endpoint/getportalmoduledetails_api.js b/pages/api/endpoint/getportalmoduledetails_api.js index 4a127a53..90c07967 100644 --- a/pages/api/endpoint/getportalmoduledetails_api.js +++ b/pages/api/endpoint/getportalmoduledetails_api.js @@ -24,44 +24,64 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var appealType = req.query.appealType; - var caseReference = req.query.caseReference.split("'").join("''"); - var token = await getToken(); + const appealType = req.query.appealType; + const caseReference = req.query.caseReference; - //console.log("the case:", req.query, caseReference); + if (typeof appealType !== "string" || appealType.trim().length === 0) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_REQUIRED", + message: "appealType is required" + }); + } - var queryUrl = - appealType + - "?$filter=pinswg_name eq '" + - caseReference + - "'&$count=true"; + if ( + typeof caseReference !== "string" || + caseReference.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CASE_REFERENCE_REQUIRED", + message: "caseReference is required" + }); + } - queryUrl = queryUrl + getSelectQuery(appealType); + try { + const token = await getToken(); + const escapedCaseReference = caseReference.split("'").join("''"); - //console.log("test:", WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + let queryUrl = + appealType + + "?$filter=pinswg_name eq '" + + escapedCaseReference + + "'&$count=true"; - return axios - .get( + queryUrl = queryUrl + getSelectQuery(appealType); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "PORTAL_MODULE_DETAILS_FETCH_FAILED", + message: "Failed to fetch portal module details" }); + } } diff --git a/pages/api/endpoint/getportalmoduledetailsproxy_api.js b/pages/api/endpoint/getportalmoduledetailsproxy_api.js index 5b833c8c..76e4f05e 100644 --- a/pages/api/endpoint/getportalmoduledetailsproxy_api.js +++ b/pages/api/endpoint/getportalmoduledetailsproxy_api.js @@ -24,40 +24,64 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var appealType = req.query.appealType; - var caseReference = req.query.caseReference; - var token = await getToken(); + const appealType = req.query.appealType; + const caseReference = req.query.caseReference; - var queryUrl = - appealType + - "?$filter=pinswg_name eq '" + - caseReference + - "'&$count=true"; + if (typeof appealType !== "string" || appealType.trim().length === 0) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_REQUIRED", + message: "appealType is required" + }); + } - queryUrl = queryUrl + getSelectQuery(appealType); + if ( + typeof caseReference !== "string" || + caseReference.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CASE_REFERENCE_REQUIRED", + message: "caseReference is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + const escapedCaseReference = caseReference.split("'").join("''"); + + let queryUrl = + appealType + + "?$filter=pinswg_name eq '" + + escapedCaseReference + + "'&$count=true"; + + queryUrl = queryUrl + getSelectQuery(appealType); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "PORTAL_MODULE_DETAILS_PROXY_FETCH_FAILED", + message: "Failed to fetch portal module details proxy" }); + } } diff --git a/pages/api/endpoint/getprojecttypes_api.js b/pages/api/endpoint/getprojecttypes_api.js index 89c681c9..8600c828 100644 --- a/pages/api/endpoint/getprojecttypes_api.js +++ b/pages/api/endpoint/getprojecttypes_api.js @@ -10,34 +10,35 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var token = await getToken(); + try { + const token = await getToken(); - var queryUrl = - "pinswg_sipsprojecttypes?$select=pinswg_name,pinswg_sipsprojecttypeid&$orderby=pinswg_name asc&$count=true"; + const queryUrl = + "pinswg_sipsprojecttypes?$select=pinswg_name,pinswg_sipsprojecttypeid&$orderby=pinswg_name asc&$count=true"; - //console.log(queryUrl); - - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "PROJECT_TYPES_FETCH_FAILED", + message: "Failed to fetch project types" }); + } } diff --git a/pages/api/endpoint/getrepresentations_api.js b/pages/api/endpoint/getrepresentations_api.js index 21422aae..fe719c55 100644 --- a/pages/api/endpoint/getrepresentations_api.js +++ b/pages/api/endpoint/getrepresentations_api.js @@ -18,36 +18,47 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var incidentID = req.query.incidentID; - var token = await getToken(); + const incidentID = req.query.incidentID; - var queryUrl = - "pinswg_representationses?$filter= _pinswg_case_value eq " + - incidentID + - " and pinswg_publishtoweb eq true&$count=true&$orderby=createdon desc"; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentID is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "pinswg_representationses?$filter= _pinswg_case_value eq " + + incidentID + + " and pinswg_publishtoweb eq true&$count=true&$orderby=createdon desc"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "REPRESENTATIONS_FETCH_FAILED", + message: "Failed to fetch representations" }); + } } diff --git a/pages/api/endpoint/getsearchdocumentTypes_api.js b/pages/api/endpoint/getsearchdocumentTypes_api.js index 222f3d06..140da38b 100644 --- a/pages/api/endpoint/getsearchdocumentTypes_api.js +++ b/pages/api/endpoint/getsearchdocumentTypes_api.js @@ -17,11 +17,11 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || @@ -57,37 +57,41 @@ const groupArray = (arr) => { }; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentid; - var token = await getToken(); + const incidentID = req.query.incidentid; - var queryUrl = - "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations"; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); + } - //console.log("docu: ", WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + try { + const token = await getToken(); - return axios - .get( + const queryUrl = + "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - //delete data["pinswg_documentid"]; - data.value.forEach(function (element) { - delete element["pinswg_documentid"]; - }); + ); - var dataArr = groupArray(data.value); - - // var dataStr; - // _.has(data, "@odata.nextLink") == true && - // ((dataStr = JSON.stringify(data["@odata.nextLink"])), - // (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(dataArr); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + data.value.forEach(function (element) { + delete element["pinswg_documentid"]; }); + + const dataArr = groupArray(data.value); + return respondSuccess(res, dataArr); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SEARCH_DOCUMENT_TYPES_FETCH_FAILED", + message: "Failed to fetch search document types" + }); + } } diff --git a/pages/api/endpoint/getsearchdocumentdetails_api.js b/pages/api/endpoint/getsearchdocumentdetails_api.js index 66a0aa56..4f924615 100644 --- a/pages/api/endpoint/getsearchdocumentdetails_api.js +++ b/pages/api/endpoint/getsearchdocumentdetails_api.js @@ -23,6 +23,7 @@ import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WORDKEY = process.env.HASHKEY; @@ -44,41 +45,53 @@ const encryptDocReference = (documentRef) => { }; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentid; - var token = await getToken(); + const incidentID = req.query.incidentid; - var queryUrl = - "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate"; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); + } - //console.log("docu: ", WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + try { + const token = await getToken(); - return axios - .get( + const queryUrl = + "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - data.value.forEach(function (element) { - Object.assign(element, { - "pinswg_documentpublisheddate": - element.pinswg_documentpublisheddate != null - ? element.pinswg_documentpublisheddate - : element.pinswg_latestpublisheddate - }); - element.pinswg_hashlink = encryptDocReference( - element.pinswg_isharedocumentreference - ); + ); + + data.value.forEach(function (element) { + Object.assign(element, { + "pinswg_documentpublisheddate": + element.pinswg_documentpublisheddate != null + ? element.pinswg_documentpublisheddate + : element.pinswg_latestpublisheddate }); - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + element.pinswg_hashlink = encryptDocReference( + element.pinswg_isharedocumentreference + ); }); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SEARCH_DOCUMENT_DETAILS_FETCH_FAILED", + message: "Failed to fetch search document details" + }); + } } diff --git a/pages/api/endpoint/getsearchdocumentdetailspaged_api.js b/pages/api/endpoint/getsearchdocumentdetailspaged_api.js index 677e405c..8533d6fb 100644 --- a/pages/api/endpoint/getsearchdocumentdetailspaged_api.js +++ b/pages/api/endpoint/getsearchdocumentdetailspaged_api.js @@ -49,6 +49,7 @@ import { azureHeadersPagedCustom } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WORDKEY = process.env.HASHKEY; @@ -70,20 +71,53 @@ const encryptDocReference = (documentRef) => { }; export default async function ApiProxy(req, res) { - var pageNumber = req.query.pageNumber; - var incidentID = req.query.incidentid; - var token = await getToken(); + const pageNumber = req.query.pageNumber; + const incidentID = req.query.incidentid; + const orderby = req.query.orderby; + const fieldSort = req.query.fieldSort; + const showNumberOfRecords = req.query.showNumberOfRecords; + const documentType = req.query.documentType || "all"; - var orderby = req.query.orderby; - var fieldSort = req.query.fieldSort; - var showNumberOfRecords = req.query.showNumberOfRecords; - var documentType = req.query.documentType || "all"; + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); + } + + if (typeof orderby !== "string" || orderby.trim().length === 0) { + return respondError(res, { + status: 400, + code: "ORDER_BY_REQUIRED", + message: "orderby is required" + }); + } + + if (typeof fieldSort !== "string" || fieldSort.trim().length === 0) { + return respondError(res, { + status: 400, + code: "FIELD_SORT_REQUIRED", + message: "fieldSort is required" + }); + } + + if ( + typeof showNumberOfRecords !== "string" || + showNumberOfRecords.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "SHOW_NUMBER_OF_RECORDS_REQUIRED", + message: "showNumberOfRecords is required" + }); + } // (documentType != "all" // ? documentType.indexOf(",")<0?" and pinswg_isharedocumentlocations eq " + documentType : // : "") + - var docTypeQueryString = ""; + let docTypeQueryString = ""; if (documentType != "all") { if (documentType.indexOf(",") >= 0) { @@ -91,8 +125,6 @@ export default async function ApiProxy(req, res) { docTypeQueryString = " and ("; documentTypeArr.forEach(function (item, index) { - console.log(item, index); - if (item != "all") { docTypeQueryString += " pinswg_isharedocumentlocations eq " + item; @@ -118,47 +150,53 @@ export default async function ApiProxy(req, res) { //console.log("has this passed docuemntType:", documentType); //(documentType !="all" && " pinswg_pinswg_isharedocumentlocations eq " + ) - var queryUrl = - "pinswg_documents?$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null" + - docTypeQueryString + - "&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + ('') - : ""); + try { + const token = await getToken(); - console.log("docu paged 1: ", queryUrl); + const queryUrl = + "pinswg_documents?$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null" + + docTypeQueryString + + "&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + + ('') + : ""); - return axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ) - .then(({ data }) => { - data.value.forEach(function (element) { - Object.assign(element, { - "pinswg_documentpublisheddate": - element.pinswg_documentpublisheddate != null - ? element.pinswg_documentpublisheddate - : element.pinswg_latestpublisheddate - }); - element.pinswg_hashlink = encryptDocReference( - element.pinswg_isharedocumentreference - ); + ); + + data.value.forEach(function (element) { + Object.assign(element, { + "pinswg_documentpublisheddate": + element.pinswg_documentpublisheddate != null + ? element.pinswg_documentpublisheddate + : element.pinswg_latestpublisheddate }); - var dataStr; - _.has(data, "@odata.nextLink") == true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - return res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + element.pinswg_hashlink = encryptDocReference( + element.pinswg_isharedocumentreference + ); }); + + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SEARCH_DOCUMENT_DETAILS_PAGED_FETCH_FAILED", + message: "Failed to fetch paged search document details" + }); + } } diff --git a/pages/api/endpoint/getsearchdocumenthistory_api.js b/pages/api/endpoint/getsearchdocumenthistory_api.js index 9c25514d..19893c0a 100644 --- a/pages/api/endpoint/getsearchdocumenthistory_api.js +++ b/pages/api/endpoint/getsearchdocumenthistory_api.js @@ -17,34 +17,45 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var documentID = req.query.documentid; - var token = await getToken(); + const documentID = req.query.documentid; - var queryUrl = - "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + - documentID; + if (typeof documentID !== "string" || documentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "DOCUMENT_ID_REQUIRED", + message: "documentid is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + const queryUrl = + "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + + documentID; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SEARCH_DOCUMENT_HISTORY_FETCH_FAILED", + message: "Failed to fetch search document history" }); + } } diff --git a/pages/api/endpoint/getsearchdocumenthistorypaged_api.js b/pages/api/endpoint/getsearchdocumenthistorypaged_api.js index 1f164445..4ee413c4 100644 --- a/pages/api/endpoint/getsearchdocumenthistorypaged_api.js +++ b/pages/api/endpoint/getsearchdocumenthistorypaged_api.js @@ -43,39 +43,50 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var documentID = req.query.documentid; - var pageNumber = req.query.pageNumber; - var token = await getToken(); + const documentID = req.query.documentid; - var queryUrl = - "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + - documentID; - // + - // (typeof pageNumber != "undefined" - // ? "&$skiptoken=" + ('') - // : ""); + if (typeof documentID !== "string" || documentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "DOCUMENT_ID_REQUIRED", + message: "documentid is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + + documentID; + // + + // (typeof pageNumber != "undefined" + // ? "&$skiptoken=" + ('') + // : ""); + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeadersPaged(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SEARCH_DOCUMENT_HISTORY_PAGED_FETCH_FAILED", + message: "Failed to fetch paged search document history" }); + } } diff --git a/pages/api/endpoint/getsipsevents_api.js b/pages/api/endpoint/getsipsevents_api.js index 42d4eb8f..b4087ded 100644 --- a/pages/api/endpoint/getsipsevents_api.js +++ b/pages/api/endpoint/getsipsevents_api.js @@ -1,33 +1,45 @@ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var caseid = req.query.caseid; - var token = await getToken(); + const caseid = req.query.caseid; - var queryUrl = - "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " + - caseid + - "&$count=true"; + if (typeof caseid !== "string" || caseid.trim().length === 0) { + return respondError(res, { + status: 400, + code: "CASE_ID_REQUIRED", + message: "caseid is required" + }); + } - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " + + caseid + + "&$count=true"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "SIPS_EVENTS_FETCH_FAILED", + message: "Failed to fetch SIPS events" }); + } } diff --git a/pages/api/endpoint/getsipsmedia_api.js b/pages/api/endpoint/getsipsmedia_api.js index eb031fd7..1ef461b4 100644 --- a/pages/api/endpoint/getsipsmedia_api.js +++ b/pages/api/endpoint/getsipsmedia_api.js @@ -1,13 +1,4 @@ -import axios from "axios"; -import CryptoJS from "crypto-js"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondSuccess } from "../middleware/apiResponse"; // export default async function ApiProxy(req, res) { // var caseid = req.query.caseid; @@ -134,5 +125,5 @@ export default function ApiProx(req, res) { } ] }; - return res.status(200).json(data); + return respondSuccess(res, data); } diff --git a/pages/api/endpoint/getwatchedcases_api.js b/pages/api/endpoint/getwatchedcases_api.js index 84f3f272..f5db25a6 100644 --- a/pages/api/endpoint/getwatchedcases_api.js +++ b/pages/api/endpoint/getwatchedcases_api.js @@ -18,71 +18,76 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var queryUrl = - "pinswg_watchlists?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$select=pinswg_emailnotifications,modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(queryUrl); - return axios - .get( + try { + const token = await getToken(); + + const queryUrl = + "pinswg_watchlists?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$select=pinswg_emailnotifications,modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; + + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - data.value.forEach(function (element) { - element.ticketnumber = element.pinswg_WatchedCase?.ticketnumber; - element.pinswg_title = - element[ - "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" - ]; + ); + data.value.forEach(function (element) { + element.ticketnumber = element.pinswg_WatchedCase?.ticketnumber; + element.pinswg_title = element[ + "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" + ]; + + element[ + "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" + ] = + element.pinswg_WatchedCase?.[ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase?.[ - "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ]; - element._pinswg_associatedlpa_value = - element.pinswg_WatchedCase?._pinswg_associatedlpa_value; - element[ + ]; + element._pinswg_associatedlpa_value = + element.pinswg_WatchedCase?._pinswg_associatedlpa_value; + element[ + "_ownerid_value@OData.Community.Display.V1.FormattedValue" + ] = + element.pinswg_WatchedCase?.[ "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase?.[ - "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ]; - element._ownerid_value = - element.pinswg_WatchedCase?._ownerid_value; + ]; + element._ownerid_value = element.pinswg_WatchedCase?._ownerid_value; - delete element.pinswg_WatchedCase; - }); - - // const uniqueItems = Array.from( - // new Map( - // data.value.map((item) => [item.ticketnumber, item]) - // ).values() - // ); - - // data = { "@odata.count": uniqueItems.length, "value": uniqueItems }; - - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + delete element.pinswg_WatchedCase; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "WATCHED_CASES_FETCH_FAILED", + message: "Failed to fetch watched cases" + }); + } } diff --git a/pages/api/endpoint/patchcase_api.js b/pages/api/endpoint/patchcase_api.js index 7daa1b1d..6d2bb2e9 100644 --- a/pages/api/endpoint/patchcase_api.js +++ b/pages/api/endpoint/patchcase_api.js @@ -11,51 +11,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; -import _ from "lodash"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentid; - var queryUrl = "incidents(" + incidentID + ")"; - var token = await getToken(); + const incidentID = req.query.incidentid; - var data = JSON.stringify({ - "servicestage": 0 - }); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); + } - //console.log(data, WEBAPI_URL + queryUrl); + try { + const queryUrl = "incidents(" + incidentID + ")"; + const token = await getToken(); - var config = { - method: "patch", - 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: data - }; + const config = { + method: "patch", + 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({ + "servicestage": 0 + }) + }; - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : axios(config) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); - }); - - return apiResponse; + const { data } = await axios(config); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_PATCH_FAILED", + message: "Failed to patch case" + }); + } } diff --git a/pages/api/endpoint/updatecase_api.js b/pages/api/endpoint/updatecase_api.js index 87038c5d..3685abbb 100644 --- a/pages/api/endpoint/updatecase_api.js +++ b/pages/api/endpoint/updatecase_api.js @@ -11,45 +11,71 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; +import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "../middleware/apiResponse"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; export default async function ApiProxy(req, res) { - var data = JSON.stringify(req.body); - var appealObj = req.query.appealObj; - var incidentID = req.query.incident; + const updatePayload = req.body; + const appealObj = req.query.appealObj; + const updateFormCollection = req.query.updateFormCollection; - var updateFormCollection = req.query.updateFormCollection; - var queryUrl = updateFormCollection + "(" + appealObj + ")"; - - var token = await getToken(); - - var config = { - method: "patch", - 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: data - }; - - //console.log("update case:", data); - - return axios(config) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - res.status(400); + if (!updatePayload || typeof updatePayload !== "object") { + return respondError(res, { + status: 400, + code: "CASE_UPDATE_PAYLOAD_REQUIRED", + message: "Case update payload is required" }); + } + if (typeof appealObj !== "string" || appealObj.trim().length === 0) { + return respondError(res, { + status: 400, + code: "APPEAL_OBJECT_ID_REQUIRED", + message: "appealObj is required" + }); + } + if ( + typeof updateFormCollection !== "string" || + updateFormCollection.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "UPDATE_FORM_COLLECTION_REQUIRED", + message: "updateFormCollection is required" + }); + } + + try { + const queryUrl = updateFormCollection + "(" + appealObj + ")"; + const token = await getToken(); + + const config = { + method: "patch", + 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(updatePayload) + }; + + const { data } = await axios(config); + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_UPDATE_FAILED", + message: "Failed to update case" + }); + } } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index a85e5ed2..0b3e09e1 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -971,6 +971,2723 @@ test("getadvancedsearch catch path returns ADVANCED_SEARCH_FETCH_FAILED", async ); }); +test("getadvancedsearchpaged returns SEARCH_STRING_REQUIRED when searchstring missing", async () => { + const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED"); +}); + +test("getadvancedsearchpaged returns ORDER_BY_REQUIRED when orderby missing", async () => { + const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + searchstring: encodeURI(JSON.stringify({ q: "cas" })), + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "ORDER_BY_REQUIRED"); +}); + +test("getadvancedsearchpaged catch path returns ADVANCED_SEARCH_PAGED_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + searchstring: encodeURI(JSON.stringify({ q: "cas" })), + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "ADVANCED_SEARCH_PAGED_FETCH_FAILED" + ); +}); + +test("getsearchdocumenthistory returns DOCUMENT_ID_REQUIRED when documentid missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumenthistory_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "DOCUMENT_ID_REQUIRED"); +}); + +test("getsearchdocumenthistory catch path returns SEARCH_DOCUMENT_HISTORY_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumenthistory_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + } + ); + + const req = { query: { documentid: "d1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SEARCH_DOCUMENT_HISTORY_FETCH_FAILED" + ); +}); + +test("getsearchdocumenthistory success returns existing data payload contract", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumenthistory_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => ({ + data: { value: [{ _pinswg_documentid_value: "d1" }] } + }) + }, + consoleLogger: () => {} + } + ); + + const req = { query: { documentid: "d1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { + value: [{ _pinswg_documentid_value: "d1" }] + }); +}); + +test("getsearchdocumenthistorypaged returns DOCUMENT_ID_REQUIRED when documentid missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumenthistorypaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "DOCUMENT_ID_REQUIRED"); +}); + +test("getsearchdocumenthistorypaged catch path returns SEARCH_DOCUMENT_HISTORY_PAGED_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumenthistorypaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + } + ); + + const req = { query: { documentid: "d1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SEARCH_DOCUMENT_HISTORY_PAGED_FETCH_FAILED" + ); +}); + +test("getsearchdocumentdetails returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumentdetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + CryptoJS: { + HmacSHA256: () => ({ toString: () => "hash" }), + enc: { Hex: { parse: () => "parsed", stringify: () => "hex" } } + }, + _: { has: () => false } + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getsearchdocumentdetails catch path returns SEARCH_DOCUMENT_DETAILS_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumentdetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + CryptoJS: { + HmacSHA256: () => ({ toString: () => "hash" }), + enc: { Hex: { parse: () => "parsed", stringify: () => "hex" } } + }, + _: { has: () => false } + } + ); + + const req = { query: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SEARCH_DOCUMENT_DETAILS_FETCH_FAILED" + ); +}); + +test("getsearchdocumentdetailspaged returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumentdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + CryptoJS: { + HmacSHA256: () => ({ toString: () => "hash" }), + enc: { Hex: { parse: () => "parsed", stringify: () => "hex" } } + }, + _: { has: () => false } + } + ); + + const req = { + query: { + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getsearchdocumentdetailspaged catch path returns SEARCH_DOCUMENT_DETAILS_PAGED_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getsearchdocumentdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + CryptoJS: { + HmacSHA256: () => ({ toString: () => "hash" }), + enc: { Hex: { parse: () => "parsed", stringify: () => "hex" } } + }, + _: { has: () => false } + } + ); + + const req = { + query: { + incidentid: "i1", + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SEARCH_DOCUMENT_DETAILS_PAGED_FETCH_FAILED" + ); +}); + +test("getsearchdocumentTypes returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getsearchdocumentTypes catch path returns SEARCH_DOCUMENT_TYPES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SEARCH_DOCUMENT_TYPES_FETCH_FAILED" + ); +}); + +test("getsearchdocumentTypes success returns grouped data payload contract", async () => { + const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => ({ + data: { + value: [ + { + pinswg_isharedocumentlocations: 1, + "pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue": + "Plans" + }, + { + pinswg_isharedocumentlocations: 1, + "pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue": + "Plans" + }, + { + pinswg_isharedocumentlocations: 2, + "pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue": + "Letters" + } + ] + } + }) + }, + consoleLogger: () => {} + }); + + const req = { query: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), [ + { + pinswg_isharedocumentlocations: 1, + pinswg_isharedocumentlocationsLabel: "Plans", + count: 2 + }, + { + pinswg_isharedocumentlocations: 2, + pinswg_isharedocumentlocationsLabel: "Letters", + count: 1 + } + ]); +}); + +test("getmycases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getmycases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getmycases catch path returns MY_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getmycases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "MY_CASES_FETCH_FAILED"); +}); + +test("getmyrepresentations returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getmyrepresentations catch path returns MY_REPRESENTATIONS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "MY_REPRESENTATIONS_FETCH_FAILED" + ); +}); + +test("getwatchedcases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getwatchedcases catch path returns WATCHED_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASES_FETCH_FAILED" + ); +}); + +test("getawaitingsubmission returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getawaitingsubmission catch path returns AWAITING_SUBMISSION_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "AWAITING_SUBMISSION_FETCH_FAILED" + ); +}); + +test("getbasicsearchdetails returns APPEAL_TYPE_NAME_REQUIRED when appealTypeName missing", async () => { + const mod = loadModule("pages/api/endpoint/getbasicsearchdetails_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=title", + getNavigationPropertyByPrimaryAttribute: () => ({ + NavigationProperty: "pinswg_TestNav" + }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { + query: { + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_TYPE_NAME_REQUIRED" + ); +}); + +test("getbasicsearchdetails catch path returns BASIC_SEARCH_DETAILS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getbasicsearchdetails_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=title", + getNavigationPropertyByPrimaryAttribute: () => ({ + NavigationProperty: "pinswg_TestNav" + }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { + query: { + appealTypeName: "pinswg_planningappeals78s", + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_SEARCH_DETAILS_FETCH_FAILED" + ); +}); + +test("getbasicsearchdetailspaged returns APPEAL_TYPE_NAME_REQUIRED when appealTypeName missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearchdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=title", + getNavigationPropertyByPrimaryAttribute: () => ({ + NavigationProperty: "pinswg_TestNav" + }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + } + ); + + const req = { + query: { + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_TYPE_NAME_REQUIRED" + ); +}); + +test("getbasicsearchdetailspaged catch path returns BASIC_SEARCH_DETAILS_PAGED_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearchdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=title", + getNavigationPropertyByPrimaryAttribute: () => ({ + NavigationProperty: "pinswg_TestNav" + }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + } + ); + + const req = { + query: { + appealTypeName: "pinswg_planningappeals78s", + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_SEARCH_DETAILS_PAGED_FETCH_FAILED" + ); +}); + +test("getbasicsearchpaged returns ORDER_BY_REQUIRED when orderby missing", async () => { + const mod = loadModule("pages/api/endpoint/getbasicsearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + searchString: "cas", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "ORDER_BY_REQUIRED"); +}); + +test("getbasicsearchpaged catch path returns BASIC_SEARCH_PAGED_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getbasicsearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + searchString: "cas", + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_SEARCH_PAGED_FETCH_FAILED" + ); +}); + +test("getbasicsearch_by_lparref returns LPA_REF_REQUIRED when lpaRef missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearch_by_lparref_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "LPA_REF_REQUIRED"); +}); + +test("getbasicsearch_by_lparref catch path returns BASIC_SEARCH_LPA_REF_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearch_by_lparref_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => { + throw new Error("token failed"); + }, + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: { lpaRef: "abc" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_SEARCH_LPA_REF_FETCH_FAILED" + ); +}); + +test("getbasicsearch_by_address success returns existing payload contract", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearch_by_address_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => ({ data: { value: [] } }) + }, + consoleLogger: () => {}, + jsonpath: () => [] + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { + "@odata.count": 0, + value: [] + }); +}); + +test("getbasicsearch_by_address catch path returns BASIC_SEARCH_BY_ADDRESS_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicsearch_by_address_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => { + throw new Error("token failed"); + }, + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => ({ data: { value: [] } }) + }, + consoleLogger: () => {}, + jsonpath: () => [] + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 500); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_SEARCH_BY_ADDRESS_FETCH_FAILED" + ); +}); + +test("getbasicdnssearch catch path returns BASIC_DNS_SEARCH_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getbasicdnssearch_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_DNS_SEARCH_FETCH_FAILED" + ); +}); + +test("getbasicdnssearchpaged returns ORDER_BY_REQUIRED when orderby missing", async () => { + const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: { fieldSort: "asc", showNumberOfRecords: "10" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "ORDER_BY_REQUIRED"); +}); + +test("getbasicdnssearchpaged catch path returns BASIC_DNS_SEARCH_PAGED_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPagedCustom: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { + query: { + orderby: "createdon", + fieldSort: "asc", + showNumberOfRecords: "10" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_DNS_SEARCH_PAGED_FETCH_FAILED" + ); +}); + +test("getbasicdnssearchdetails returns CASE_REFERENCE_REQUIRED when caseReference missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicdnssearchdetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CASE_REFERENCE_REQUIRED" + ); +}); + +test("getbasicdnssearchdetails catch path returns BASIC_DNS_SEARCH_DETAILS_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicdnssearchdetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + } + ); + + const req = { query: { caseReference: "DNS-001" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_DNS_SEARCH_DETAILS_FETCH_FAILED" + ); +}); + +test("getbasicdnssearchdetailspaged returns CASE_REFERENCE_REQUIRED when caseReference missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicdnssearchdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CASE_REFERENCE_REQUIRED" + ); +}); + +test("getbasicdnssearchdetailspaged catch path returns BASIC_DNS_SEARCH_DETAILS_PAGED_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicdnssearchdetailspaged_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + } + ); + + const req = { query: { caseReference: "DNS-001" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_DNS_SEARCH_DETAILS_PAGED_FETCH_FAILED" + ); +}); + +test("getportalmoduledetails returns APPEAL_TYPE_REQUIRED when appealType missing", async () => { + const mod = loadModule("pages/api/endpoint/getportalmoduledetails_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: { caseReference: "CAS-1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "APPEAL_TYPE_REQUIRED"); +}); + +test("getportalmoduledetails catch path returns PORTAL_MODULE_DETAILS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getportalmoduledetails_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { + query: { + appealType: "pinswg_planningappeals78s", + caseReference: "CAS-1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "PORTAL_MODULE_DETAILS_FETCH_FAILED" + ); +}); + +test("getportalmoduledetailsproxy returns APPEAL_TYPE_REQUIRED when appealType missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getportalmoduledetailsproxy_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { query: { caseReference: "CAS-1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "APPEAL_TYPE_REQUIRED"); +}); + +test("getportalmoduledetailsproxy catch path returns PORTAL_MODULE_DETAILS_PROXY_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getportalmoduledetailsproxy_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getSelectQuery: () => "&$select=pinswg_name", + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + } + ); + + const req = { + query: { + appealType: "pinswg_planningappeals78s", + caseReference: "CAS-1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "PORTAL_MODULE_DETAILS_PROXY_FETCH_FAILED" + ); +}); + +test("getmylpacases returns LPA_ID_REQUIRED when lpaid missing", async () => { + const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getLPA: async () => [{ name: "Test LPA", accountid: "acc1" }], + jsonpath: () => [{ accountid: "acc1" }], + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "LPA_ID_REQUIRED"); +}); + +test("getmylpacases returns LPA_NOT_FOUND when lpaid lookup misses", async () => { + const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getLPA: async () => [{ name: "Another LPA", accountid: "acc1" }], + jsonpath: () => [], + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: { lpaid: "Missing LPA" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 404); + assert.strictEqual(res.state.jsonBody.error.code, "LPA_NOT_FOUND"); +}); + +test("getmylpacases catch path returns MY_LPA_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getLPA: async () => [{ name: "Test LPA", accountid: "acc1" }], + jsonpath: () => [{ accountid: "acc1" }], + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { lpaid: "Test LPA" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "MY_LPA_CASES_FETCH_FAILED" + ); +}); + +test("getcase returns INCIDENT_ID_REQUIRED when incidentID missing", async () => { + const mod = loadModule("pages/api/endpoint/getcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { ticketnumber: "CAS-1" } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getcase catch path returns CASE_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: { incidentID: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_FETCH_FAILED"); +}); + +test("getcasebyid returns INCIDENT_ID_REQUIRED when incidentID missing", async () => { + const mod = loadModule("pages/api/endpoint/getcasebyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { ticketnumber: "CAS-1" } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getcasebyid catch path returns CASE_BY_ID_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getcasebyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: { incidentID: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CASE_BY_ID_FETCH_FAILED" + ); +}); + +test("getincidentbyid returns SEARCH_STRING_REQUIRED when searchString missing", async () => { + const mod = loadModule("pages/api/endpoint/getincidentbyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED"); +}); + +test("getincidentbyid catch path returns INCIDENT_BY_ID_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getincidentbyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { searchString: "incident-guid" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "INCIDENT_BY_ID_FETCH_FAILED" + ); +}); + +test("createwatchedcases returns WATCHED_CASE_BINDINGS_REQUIRED when bindings missing", async () => { + const mod = loadModule("pages/api/endpoint/createwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { body: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_BINDINGS_REQUIRED" + ); +}); + +test("createwatchedcases returns INVALID_BINDING_FORMAT when bindings invalid", async () => { + const mod = loadModule("pages/api/endpoint/createwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { + body: { + "pinswg_WatchedCase@odata.bind": "/incidents", + "pinswg_Contact@odata.bind": "/contacts" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INVALID_BINDING_FORMAT"); +}); + +test("createwatchedcases catch path returns WATCHED_CASE_UPSERT_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/createwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => { + throw new Error("token failed"); + }, + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { + body: { + "pinswg_WatchedCase@odata.bind": "/incidents(incident-id)", + "pinswg_Contact@odata.bind": "/contacts(contact-id)" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_UPSERT_FAILED" + ); +}); + +test("deletewatchedcases returns WATCHED_CASE_ID_REQUIRED when watchedCaseID missing", async () => { + const mod = loadModule("pages/api/endpoint/deletewatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_ID_REQUIRED" + ); +}); + +test("deletewatchedcases catch path returns WATCHED_CASE_DELETE_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/deletewatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("delete failed"); + }, + consoleLogger: () => {} + }); + + const req = { query: { watchedCaseID: "w1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_DELETE_FAILED" + ); +}); + +test("deletewatchedcasesproxy returns WATCHED_CASE_ID_REQUIRED when watchedCaseID missing", async () => { + const mod = loadModule( + "pages/api/endpoint/deletewatchedcasesproxy_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: {} }) }, + consoleLogger: () => {}, + process: { env: {} } + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_ID_REQUIRED" + ); +}); + +test("deletewatchedcasesproxy catch path returns WATCHED_CASE_PROXY_DELETE_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/deletewatchedcasesproxy_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("proxy delete failed"); + } + }, + consoleLogger: () => {}, + process: { env: {} } + } + ); + + const req = { query: { watchedCaseID: "w1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASE_PROXY_DELETE_FAILED" + ); +}); + +test("deletemyrepresentations returns MY_REPRESENTATION_ID_REQUIRED when myRepresentationsID missing", async () => { + const mod = loadModule( + "pages/api/endpoint/deletemyrepresentations_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "MY_REPRESENTATION_ID_REQUIRED" + ); +}); + +test("deletemyrepresentations catch path returns MY_REPRESENTATION_DELETE_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/deletemyrepresentations_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("delete failed"); + }, + consoleLogger: () => {} + } + ); + + const req = { query: { myRepresentationsID: "r1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "MY_REPRESENTATION_DELETE_FAILED" + ); +}); + +test("deleteawaitingsubmissions returns INCIDENT_ID_REQUIRED when incidentID missing", async () => { + const mod = loadModule( + "pages/api/endpoint/deleteawaitingsubmissions_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("deleteawaitingsubmissions catch path returns AWAITING_SUBMISSION_DELETE_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/deleteawaitingsubmissions_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("delete failed"); + }, + consoleLogger: () => {} + } + ); + + const req = { query: { incidentID: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "AWAITING_SUBMISSION_DELETE_FAILED" + ); +}); + +test("getlpa catch path returns LPA_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getlpa_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "LPA_FETCH_FAILED"); +}); + +test("getappealid returns CASE_REFERENCE_REQUIRED when caseReference missing", async () => { + const mod = loadModule("pages/api/endpoint/getappealid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { + query: { + updateFormCollection: "pinswg_planningappeals78s", + primaryAttribute: "pinswg_planningappeals78id" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CASE_REFERENCE_REQUIRED" + ); +}); + +test("getappealid catch path returns APPEAL_ID_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getappealid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { + query: { + caseReference: "CAS-1", + updateFormCollection: "pinswg_planningappeals78s", + primaryAttribute: "pinswg_planningappeals78id" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "APPEAL_ID_FETCH_FAILED"); +}); + +test("getprojecttypes catch path returns PROJECT_TYPES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getprojecttypes_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "PROJECT_TYPES_FETCH_FAILED" + ); +}); + +test("getappealtypes catch path returns APPEAL_TYPES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getappealtypes_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + process: { env: {} } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_TYPES_FETCH_FAILED" + ); +}); + +test("getlinkedcases returns PARENT_INCIDENT_ID_REQUIRED when parentincidentid missing", async () => { + const mod = loadModule("pages/api/endpoint/getlinkedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "PARENT_INCIDENT_ID_REQUIRED" + ); +}); + +test("getlinkedcases catch path returns LINKED_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getlinkedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { parentincidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LINKED_CASES_FETCH_FAILED" + ); +}); + +test("getformdata returns WHICH_FORM_REQUIRED when whichForm missing", async () => { + const mod = loadModule("pages/api/endpoint/getformdata_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "WHICH_FORM_REQUIRED"); +}); + +test("getformdata catch path returns FORM_DATA_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getformdata_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { whichForm: "planningappeals78" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "FORM_DATA_FETCH_FAILED"); +}); + +test("getispublishedbyid returns SEARCH_STRING_REQUIRED when searchString missing", async () => { + const mod = loadModule("pages/api/endpoint/getispublishedbyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED"); +}); + +test("getispublishedbyid catch path returns IS_PUBLISHED_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getispublishedbyid_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { searchString: "incident-guid" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "IS_PUBLISHED_FETCH_FAILED" + ); +}); + +test("getdnslist catch path returns DNS_LIST_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getdnslist_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "DNS_LIST_FETCH_FAILED"); +}); + +test("getsipsevents returns CASE_ID_REQUIRED when caseid missing", async () => { + const mod = loadModule("pages/api/endpoint/getsipsevents_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_ID_REQUIRED"); +}); + +test("getsipsevents catch path returns SIPS_EVENTS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getsipsevents_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { caseid: "case-guid" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "SIPS_EVENTS_FETCH_FAILED" + ); +}); + +test("getbasicpartsaveddetails returns APPEAL_TYPE_NAME_REQUIRED when appealTypeName missing", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicpartsaveddetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersNoOdata: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + } + ); + + const req = { + query: { + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_TYPE_NAME_REQUIRED" + ); +}); + +test("getbasicpartsaveddetails catch path returns BASIC_PART_SAVED_DETAILS_FETCH_FAILED", async () => { + const mod = loadModule( + "pages/api/endpoint/getbasicpartsaveddetails_api.js", + { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersNoOdata: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + } + ); + + const req = { + query: { + appealTypeName: "pinswg_planningappeals78s", + primaryIdAttribute: "pinswg_planningappeals78id", + incidentID: "i1" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_PART_SAVED_DETAILS_FETCH_FAILED" + ); +}); + +test("getbasicdnsurlsearch returns SEARCH_STRING_REQUIRED when searchString missing", async () => { + const mod = loadModule("pages/api/endpoint/getbasicdnsurlsearch_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED"); +}); + +test("getbasicdnsurlsearch catch path returns BASIC_DNS_URL_SEARCH_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getbasicdnsurlsearch_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: { searchString: "farm" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "BASIC_DNS_URL_SEARCH_FETCH_FAILED" + ); +}); + +test("getpartsavedappeal returns SEARCH_STRING_REQUIRED when searchString missing", async () => { + const mod = loadModule("pages/api/endpoint/getpartsavedappeal_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED"); +}); + +test("getpartsavedappeal catch path returns PART_SAVED_APPEAL_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getpartsavedappeal_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeadersPaged: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + _: { has: () => false } + }); + + const req = { query: { searchString: "CAS" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "PART_SAVED_APPEAL_FETCH_FAILED" + ); +}); + +test("getrepresentations returns INCIDENT_ID_REQUIRED when incidentID missing", async () => { + const mod = loadModule("pages/api/endpoint/getrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getrepresentations catch path returns REPRESENTATIONS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { incidentID: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "REPRESENTATIONS_FETCH_FAILED" + ); +}); + +test("getdnscoords catch path returns DNS_COORDS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getdnscoords_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {}, + OSPoint: function () {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "DNS_COORDS_FETCH_FAILED" + ); +}); + +test("getappealpdfdocuments returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("getappealpdfdocuments catch path returns APPEAL_PDF_DOCUMENTS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_PDF_DOCUMENTS_FETCH_FAILED" + ); +}); + +test("createcase returns CASE_PAYLOAD_REQUIRED when body missing", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: { ticketnumber: "CAS-1" } }), + consoleLogger: () => {} + }); + + const req = { query: {}, body: undefined }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_PAYLOAD_REQUIRED"); +}); + +test("createcase returns CONTACT_ID_REQUIRED when contactid missing", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: { ticketnumber: "CAS-1" } }), + consoleLogger: () => {} + }); + + const req = { + query: { appealTypeId: "a1", containername: "c1", lpaID: "l1" }, + body: { title: "x" } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CONTACT_ID_REQUIRED"); +}); + +test("createcase catch path returns CASE_CREATE_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("create failed"); + }, + consoleLogger: () => {} + }); + + const req = { + query: { + contactid: "c1", + appealTypeId: "a1", + containername: "container", + lpaID: "l1" + }, + body: { title: "x" } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_CREATE_FAILED"); +}); + +test("updatecase returns CASE_UPDATE_PAYLOAD_REQUIRED when body missing", async () => { + const mod = loadModule("pages/api/endpoint/updatecase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { + query: { appealObj: "a1", updateFormCollection: "pinswg_x" }, + body: undefined + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CASE_UPDATE_PAYLOAD_REQUIRED" + ); +}); + +test("updatecase catch path returns CASE_UPDATE_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/updatecase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("update failed"); + }, + consoleLogger: () => {} + }); + + const req = { + query: { appealObj: "a1", updateFormCollection: "pinswg_x" }, + body: { title: "updated" } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_UPDATE_FAILED"); +}); + +test("patchcase returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule("pages/api/endpoint/patchcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED"); +}); + +test("patchcase catch path returns CASE_PATCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/patchcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("patch failed"); + }, + consoleLogger: () => {} + }); + + const req = { query: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_PATCH_FAILED"); +}); + +test("createcrmtask returns CONTACT_EMAIL_AND_SUBJECT_REQUIRED when required fields missing", async () => { + const mod = loadModule("pages/api/endpoint/createcrmtask_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + isNonEmptyString: () => false, + sanitizeString: (v) => v, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: {} }), + consoleLogger: () => {} + }); + + const req = { body: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "CONTACT_EMAIL_AND_SUBJECT_REQUIRED" + ); +}); + +test("createcrmtask catch path returns CRM_TASK_CREATE_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/createcrmtask_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + isNonEmptyString: () => true, + sanitizeString: (v) => v, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("create failed"); + }, + consoleLogger: () => {} + }); + + const req = { + body: { + contactEmail: "user@test.local", + contactSubject: "Need help", + contactbody: "Details", + typeOfContact: "plq" + } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CRM_TASK_CREATE_FAILED"); +}); + +test("gethash returns UNAUTHENTICATED when session missing", async () => { + const mod = loadModule("pages/api/endpoint/gethash_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + hashAPIPath: () => "&hash=expected", + getSession: async () => null, + nextConnect: () => { + const handler = { registeredGet: null }; + handler.get = (cb) => { + handler.registeredGet = cb; + return handler; + }; + return handler; + } + }); + + const req = { query: { path: "/api/endpoint/getportallogin_api?x=1" } }; + const res = createRes(); + await mod.default.registeredGet(req, res); + + assert.strictEqual(res.state.statusCode, 401); + assert.strictEqual(res.state.jsonBody.error.code, "UNAUTHENTICATED"); +}); + +test("gethash returns INVALID_HASH_PATH when path is invalid", async () => { + const mod = loadModule("pages/api/endpoint/gethash_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + hashAPIPath: () => "&hash=expected", + getSession: async () => ({ user: { email: "user@test.local" } }), + nextConnect: () => { + const handler = { registeredGet: null }; + handler.get = (cb) => { + handler.registeredGet = cb; + return handler; + }; + return handler; + } + }); + + const req = { query: { path: "/api/endpoint/gethash_api" } }; + const res = createRes(); + await mod.default.registeredGet(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH_PATH"); +}); + +test("getpicklists returns WHICH_FORM_REQUIRED when whichForm missing", async () => { + const mod = loadModule("pages/api/endpoint/getpicklists_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "WHICH_FORM_REQUIRED"); +}); + +test("getmandatoryfields returns WHICH_FORM_REQUIRED when whichForm missing", async () => { + const mod = loadModule("pages/api/endpoint/getmandatoryfields_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "WHICH_FORM_REQUIRED"); +}); + +test("getappealtypesfornewappeal returns success payload contract", async () => { + const mod = loadModule( + "pages/api/endpoint/getappealtypesfornewappeal_api.js", + { + respondSuccess: respondSuccessMock + } + ); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.strictEqual(Array.isArray(res.state.jsonBody.value), true); + assert.strictEqual( + res.state.jsonBody.value[0].value, + "Planning Appeal - S78" + ); +}); + +test("getToken success returns token payload contract", async () => { + const mod = loadModule("pages/api/endpoint/getToken.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + axios: { + post: async () => ({ + data: { + access_token: "token", + token_type: "Bearer" + } + }) + }, + process: { + env: { + ACCESS_TOKEN_ENDPOINT: "https://login.microsoftonline.com/", + TENANT: "tenant-id", + GRANT_TYPE: "client_credentials", + CLIENT_ID: "client-id", + CLIENT_SECRET: "secret", + RELAYURI: "relay.example" + } + } + }); + + const req = { body: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), { + access_token: "token", + token_type: "Bearer" + }); +}); + +test("getToken catch path returns TOKEN_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getToken.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + axios: { + post: async () => { + throw new Error("token fetch failed"); + } + }, + process: { + env: { + ACCESS_TOKEN_ENDPOINT: "https://login.microsoftonline.com/", + TENANT: "tenant-id", + GRANT_TYPE: "client_credentials", + CLIENT_ID: "client-id", + CLIENT_SECRET: "secret", + RELAYURI: "relay.example" + } + } + }); + + const req = { body: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "TOKEN_FETCH_FAILED"); +}); + +test("getsipsmedia returns success contract", async () => { + const mod = loadModule("pages/api/endpoint/getsipsmedia_api.js", { + respondSuccess: respondSuccessMock + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.strictEqual(res.state.jsonBody["@odata.count"], 4); +}); + const run = async () => { let passed = 0; for (const currentTest of tests) {