Skip to content

Commit

Permalink
feat: Send Swagger docs (#2445)
Browse files Browse the repository at this point in the history
* chore: Move send to module

* chore: Consolidate existing docs

* chore: Move to subfolders

* feat: Document and add validation layer to create send events endpoint

* test: Update test import refs

* feat: Donwload application files

* fix: Linting after rebase
  • Loading branch information
DafyddLlyr authored Nov 18, 2023
1 parent ec449ba commit a706501
Show file tree
Hide file tree
Showing 23 changed files with 560 additions and 445 deletions.
6 changes: 6 additions & 0 deletions api.planx.uk/lib/hasura/metadata/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ interface ScheduledEvent {
args: ScheduledEventArgs;
}

export interface CombinedResponse {
bops?: ScheduledEventResponse;
uniform?: ScheduledEventResponse;
email?: ScheduledEventResponse;
}

interface ScheduledEventArgs {
headers: Record<string, string>[];
retry_conf: {
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/zip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import supertest from "supertest";
import app from "../../../server";
import { authHeader } from "../../../tests/mockJWT";

jest.mock("../../../send/exportZip", () => ({
jest.mock("../../send/utils/exportZip", () => ({
buildSubmissionExportZip: jest.fn().mockResolvedValue({
filename: "tests/mocks/test.zip",
remove: jest.fn,
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/admin/session/zip.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextFunction, Request, Response } from "express";
import { buildSubmissionExportZip } from "../../../send/exportZip";
import { buildSubmissionExportZip } from "../../send/utils/exportZip";

/**
* @swagger
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/modules/pay/controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from "assert";
import { Request } from "express";
import { responseInterceptor } from "http-proxy-middleware";
import { logPaymentStatus } from "../../send/helpers";
import { logPaymentStatus } from "../send/utils/helpers";
import { usePayProxy } from "./proxy";
import { $api } from "../../client";
import { ServerError } from "../../errors";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ComponentType } from "@opensystemslab/planx-core/types";
import { NextFunction, Request, Response } from "express";
import { gql } from "graphql-request";
import { $api, $public } from "../../../../client";
import {
ScheduledEventResponse,
CombinedResponse,
createScheduledEvent,
} from "../../../../lib/hasura/metadata";
import { $api, $public } from "../../../../client";
import { getMostRecentPublishedFlow } from "../../../../helpers";
import { Flow, Node, Team } from "../../../../types";

Expand All @@ -15,12 +15,6 @@ enum Destination {
Email = "email",
}

interface CombinedResponse {
bops?: ScheduledEventResponse;
uniform?: ScheduledEventResponse;
email?: ScheduledEventResponse;
}

// Create "One-off Scheduled Events" in Hasura when a payment request is paid
const createPaymentSendEvents = async (
req: Request,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import nock from "nock";
import supertest from "supertest";
import { queryMock } from "../tests/graphqlQueryMock";
import app from "../server";
import { expectedPayload } from "../tests/mocks/bopsMocks";
import { queryMock } from "../../../tests/graphqlQueryMock";
import app from "../../../server";
import { expectedPayload } from "../../../tests/mocks/bopsMocks";

jest.mock("../modules/saveAndReturn/service/utils", () => ({
jest.mock("../../saveAndReturn/service/utils", () => ({
markSessionAsSubmitted: jest.fn(),
}));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import axios, { AxiosResponse } from "axios";
import { markSessionAsSubmitted } from "../modules/saveAndReturn/service/utils";
import { markSessionAsSubmitted } from "../../saveAndReturn/service/utils";
import { NextFunction, Request, Response } from "express";
import { gql } from "graphql-request";
import { $api } from "../client";
import { ServerError } from "../errors";
import { $api } from "../../../client";
import { ServerError } from "../../../errors";

interface SendToBOPSRequest {
payload: {
Expand All @@ -17,27 +17,6 @@ interface CreateBopsApplication {
bopsId: string;
};
}

/**
* @swagger
* /bops/{localAuthority}:
* post:
* summary: Submits an application to the Back Office Planning System (BOPS)
* description: Submits an application to the Back Office Planning System (BOPS)
* tags:
* - submissions
* parameters:
* - $ref: '#/components/parameters/localAuthority'
* security:
* - hasuraAuth: []
* requestBody:
* description: This endpoint is only called via Hasura's scheduled event webhook, so body is wrapped in a `payload` key
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SessionPayload'
*/
const sendToBOPS = async (req: Request, res: Response, next: NextFunction) => {
// `/bops/:localAuthority` is only called via Hasura's scheduled event webhook now, so body is wrapped in a "payload" key
const { payload }: SendToBOPSRequest = req.body;
Expand Down
59 changes: 59 additions & 0 deletions api.planx.uk/modules/send/createSendEvents/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
CombinedResponse,
createScheduledEvent,
} from "../../../lib/hasura/metadata";
import { CreateSendEventsController } from "./types";

// Create "One-off Scheduled Events" in Hasura from Send component for selected destinations
const createSendEvents: CreateSendEventsController = async (
_req,
res,
next,
) => {
const { email, uniform, bops } = res.locals.parsedReq.body;
const { sessionId } = res.locals.parsedReq.params;

try {
const now = new Date();
const combinedResponse: CombinedResponse = {};

if (email) {
const emailSubmissionEvent = await createScheduledEvent({
webhook: `{{HASURA_PLANX_API_URL}}/email-submission/${email.localAuthority}`,
schedule_at: now,
payload: email.body,
comment: `email_submission_${sessionId}`,
});
combinedResponse["email"] = emailSubmissionEvent;
}

if (bops) {
const bopsEvent = await createScheduledEvent({
webhook: `{{HASURA_PLANX_API_URL}}/bops/${bops.localAuthority}`,
schedule_at: new Date(now.getTime() + 30 * 1000),
payload: bops.body,
comment: `bops_submission_${sessionId}`,
});
combinedResponse["bops"] = bopsEvent;
}

if (uniform) {
const uniformEvent = await createScheduledEvent({
webhook: `{{HASURA_PLANX_API_URL}}/uniform/${uniform.localAuthority}`,
schedule_at: new Date(now.getTime() + 60 * 1000),
payload: uniform.body,
comment: `uniform_submission_${sessionId}`,
});
combinedResponse["uniform"] = uniformEvent;
}

return res.json(combinedResponse);
} catch (error) {
return next({
error,
message: `Failed to create send event(s) for session ${sessionId}. Error: ${error}`,
});
}
};

export { createSendEvents };
26 changes: 26 additions & 0 deletions api.planx.uk/modules/send/createSendEvents/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { z } from "zod";
import { CombinedResponse } from "../../../lib/hasura/metadata";
import { ValidatedRequestHandler } from "../../../shared/middleware/validate";

const eventSchema = z.object({
localAuthority: z.string(),
body: z.object({
sessionId: z.string().uuid(),
}),
});

export const combinedEventsPayloadSchema = z.object({
body: z.object({
email: eventSchema.optional(),
bops: eventSchema.optional(),
uniform: eventSchema.optional(),
}),
params: z.object({
sessionId: z.string().uuid(),
}),
});

export type CreateSendEventsController = ValidatedRequestHandler<
typeof combinedEventsPayloadSchema,
CombinedResponse
>;
138 changes: 138 additions & 0 deletions api.planx.uk/modules/send/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
info:
title: Plan✕ API
version: 0.1.0
tags:
- name: send
components:
schemas:
EventSchema:
type: object
properties:
localAuthority:
type: string
body:
type: object
properties:
sessionId:
type: string
format: uuid
required:
- localAuthority
- body
paths:
/bops/{localAuthority}:
post:
summary: Submits an application to the Back Office Planning System (BOPS)
description: Submits an application to the Back Office Planning System (BOPS)
tags:
- send
parameters:
- $ref: "#/components/parameters/localAuthority"
security:
- hasuraAuth: []
requestBody:
description: This endpoint is only called via Hasura's scheduled event webhook, so body is wrapped in a `payload` key
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SessionPayload"
/email-submission/{localAuthority}:
post:
summary: Sends an application by email using GOV.UK Notify
description: Send an application by email using GOV.UK Notify. The email body includes a link to download the application files.
tags:
- send
parameters:
- $ref: "#/components/parameters/localAuthority"
security:
- hasuraAuth: []
requestBody:
description: This endpoint is only called via Hasura's scheduled event webhook, so body is wrapped in a `payload` key
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SessionPayload"
/uniform/{localAuthority}:
post:
summary: Submits an application to Uniform
description: Submits an application to Uniform
tags:
- send
parameters:
- $ref: "#/components/parameters/localAuthority"
security:
- hasuraAuth: []
requestBody:
description: This endpoint is only called via Hasura's scheduled event webhook, so body is wrapped in a `payload` key
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SessionPayload"
/create-send-events/{sessionId}:
post:
summary: Create send events
description: Create "One-off Scheduled Events" in Hasura from Send component for selected destinations
tags:
- send
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
$ref: "#/components/schemas/EventSchema"
bops:
$ref: "#/components/schemas/EventSchema"
uniform:
$ref: "#/components/schemas/EventSchema"
required:
- email
- bops
- uniform
parameters:
- in: query
name: sessionId
required: true
schema:
type: string
format: uuid
/download-application-files/{sessionId}:
get:
summary: Download application files
description: Download application files via a link send to a team's "send to email" email address
tags:
- send
parameters:
- in: path
name: sessionId
required: true
schema:
type: string
format: uuid
- in: query
name: sessionId
required: true
schema:
type: string
format: uuid
- in: query
name: email
required: true
schema:
type: string
format: email
- in: query
name: localAuthority
required: true
schema:
type: string
responses:
"200":
$ref: "#/components/responses/DownloadFile"
"500":
$ref: "#/components/responses/ErrorMessage"
Loading

0 comments on commit a706501

Please sign in to comment.