Skip to content

Commit

Permalink
feat: Setup BOPS client
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Jan 26, 2024
1 parent d112012 commit cd6800e
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 201 deletions.
19 changes: 9 additions & 10 deletions api.planx.uk/modules/send/bops/bops.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ describe(`sending an application to BOPS`, () => {
teams: [
{
integrations: {
bopsSubmissionURL: submissionURL,
submissionURL,
secret: null,
},
},
],
Expand All @@ -78,6 +79,7 @@ describe(`sending an application to BOPS`, () => {
{
integrations: {
submissionURL: null,
secret: null,
},
},
],
Expand Down Expand Up @@ -128,7 +130,7 @@ describe(`sending an application to BOPS`, () => {
.post("/bops/unsupported-team")
.set({ Authorization: process.env.HASURA_PLANX_API_KEY! })
.send({ payload: { sessionId: "123" } })
.expect(400)
.expect(500)
.then((res) => {
expect(res.body.error).toMatch(/not enabled for this local authority/);
});
Expand Down Expand Up @@ -193,6 +195,9 @@ describe(`sending an application to BOPS v2`, () => {
{
integrations: {
submissionURL: submissionURL,
// Decodes to "abc123"
secret:
"ccd96ddbcf94af4899a1fe9c88752547:e913b3b604f0610ee57abb822e6cc6fd",
},
},
],
Expand All @@ -205,13 +210,7 @@ describe(`sending an application to BOPS v2`, () => {
queryMock.mockQuery({
name: "GetStagingBopsSubmissionDetails",
data: {
teams: [
{
integrations: {
submissionURL: null,
},
},
],
teams: [],
},
variables: {
slug: "unsupported-team",
Expand Down Expand Up @@ -259,7 +258,7 @@ describe(`sending an application to BOPS v2`, () => {
.post("/bops-v2/unsupported-team")
.set({ Authorization: process.env.HASURA_PLANX_API_KEY! })
.send({ payload: { sessionId: "123" } })
.expect(400)
.expect(500)
.then((res) => {
expect(res.body.error).toMatch(/not enabled for this local authority/);
});
Expand Down
294 changes: 103 additions & 191 deletions api.planx.uk/modules/send/bops/bops.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios, { AxiosResponse } from "axios";
import { BOPSClient } from "./bopsClient";
import { markSessionAsSubmitted } from "../../saveAndReturn/service/utils";
import { NextFunction, Request, Response } from "express";
import { gql } from "graphql-request";
Expand Down Expand Up @@ -40,106 +40,63 @@ const sendToBOPS = async (req: Request, res: Response, next: NextFunction) => {
});
}

// confirm this local authority (aka team) is supported by BOPS before creating the proxy
// a local or staging API instance should send to the BOPS staging endpoint
// production should send to the BOPS production endpoint
const localAuthority = req.params.localAuthority;
const env =
process.env.APP_ENVIRONMENT === "production" ? "production" : "staging";
const details = await $api.team.getBopsSubmissionDetails(localAuthority, env);
if (!details?.submissionURL) {
return next(
new ServerError({
status: 400,
message: `Back-office Planning System (BOPS) is not enabled for this local authority (${localAuthority})`,
}),
);
}
const target = `${details.submissionURL}/api/v1/planning_applications`;
const exportData = await $api.export.bopsPayload(payload?.sessionId);

try {
const bopsResponse = await axios({
method: "POST",
url: target,
adapter: "http",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.BOPS_API_TOKEN}`,
},
data: exportData,
})
.then(async (res: AxiosResponse<{ id: string }>) => {
// Mark session as submitted so that reminder and expiry emails are not triggered
markSessionAsSubmitted(payload?.sessionId);

const applicationId = await $api.client.request<CreateBopsApplication>(
gql`
mutation CreateBopsApplication(
$bops_id: String = ""
$destination_url: String!
$request: jsonb!
$req_headers: jsonb = {}
$response: jsonb = {}
$response_headers: jsonb = {}
$session_id: String!
) {
insertBopsApplication: insert_bops_applications_one(
object: {
bops_id: $bops_id
destination_url: $destination_url
request: $request
req_headers: $req_headers
response: $response
response_headers: $response_headers
session_id: $session_id
}
) {
id
bopsId: bops_id
}
const bopsClient = await BOPSClient.create(localAuthority);
const response = await bopsClient.postV1(payload.sessionId);

// Mark session as submitted so that reminder and expiry emails are not triggered
await markSessionAsSubmitted(payload?.sessionId);

const applicationId = await $api.client.request<CreateBopsApplication>(
gql`
mutation CreateBopsApplication(
$bops_id: String = ""
$destination_url: String!
$request: jsonb!
$req_headers: jsonb = {}
$response: jsonb = {}
$response_headers: jsonb = {}
$session_id: String!
) {
insertBopsApplication: insert_bops_applications_one(
object: {
bops_id: $bops_id
destination_url: $destination_url
request: $request
req_headers: $req_headers
response: $response
response_headers: $response_headers
session_id: $session_id
}
`,
{
bops_id: res.data.id,
destination_url: target,
request: exportData,
response: res.data,
response_headers: res.headers,
session_id: payload?.sessionId,
},
);

return {
application: {
...applicationId.insertBopsApplication,
bopsResponse: res.data,
},
};
})
.catch((error) => {
if (error.response) {
throw new Error(
`Sending to BOPS failed (${localAuthority}):\n${JSON.stringify(
error.response.data,
null,
2,
)}`,
);
} else {
// re-throw other errors
throw new Error(
`Sending to BOPS failed (${localAuthority}):\n${error}`,
);
) {
id
bopsId: bops_id
}
}
});
res.send(bopsResponse);
} catch (err) {
next(
`,
{
bops_id: response.data.id,
destination_url: response.request.url,
request: response.request.data,
response: response.data,
response_headers: response.headers,
session_id: payload?.sessionId,
},
);

res.send({
application: {
...applicationId.insertBopsApplication,
bopsResponse: response.data,
},
});
} catch (error) {
return next(
new ServerError({
status: 500,
message: `Sending to BOPS failed (${localAuthority})`,
cause: err,
message: `Sending to BOPS failed (${localAuthority}). ${error}`,
cause: error,
}),
);
}
Expand Down Expand Up @@ -171,113 +128,68 @@ const sendToBOPSV2 = async (
});
}

// confirm this local authority (aka team) is supported by BOPS before creating the proxy
// a local or staging API instance should send to the BOPS staging endpoint
// production should send to the BOPS production endpoint
const localAuthority = req.params.localAuthority;
const env =
process.env.APP_ENVIRONMENT === "production" ? "production" : "staging";
const details = await $api.team.getBopsSubmissionDetails(localAuthority, env);
if (!details?.submissionURL) {
return next(
new ServerError({
status: 400,
message: `Back-office Planning System (BOPS) is not enabled for this local authority (${localAuthority})`,
}),
);
}
const target = `${details?.submissionURL}/api/v2/planning_applications`;
const exportData = await $api.export.digitalPlanningDataPayload(
payload?.sessionId,
);

try {
const bopsResponse = await axios({
method: "POST",
url: target,
adapter: "http",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.BOPS_API_TOKEN}`,
},
data: exportData,
})
.then(async (res: AxiosResponse<{ id: string }>) => {
// Mark session as submitted so that reminder and expiry emails are not triggered
markSessionAsSubmitted(payload?.sessionId);

const applicationId = await $api.client.request<CreateBopsApplication>(
gql`
mutation CreateBopsApplication(
$bops_id: String = ""
$destination_url: String!
$request: jsonb!
$req_headers: jsonb = {}
$response: jsonb = {}
$response_headers: jsonb = {}
$session_id: String!
) {
insertBopsApplication: insert_bops_applications_one(
object: {
bops_id: $bops_id
destination_url: $destination_url
request: $request
req_headers: $req_headers
response: $response
response_headers: $response_headers
session_id: $session_id
}
) {
id
bopsId: bops_id
}
const bopsClient = await BOPSClient.create(localAuthority);
const response = await bopsClient.postV2(payload.sessionId);

// Mark session as submitted so that reminder and expiry emails are not triggered
await markSessionAsSubmitted(payload?.sessionId);

const applicationId = await $api.client.request<CreateBopsApplication>(
gql`
mutation CreateBopsApplication(
$bops_id: String = ""
$destination_url: String!
$request: jsonb!
$req_headers: jsonb = {}
$response: jsonb = {}
$response_headers: jsonb = {}
$session_id: String!
) {
insertBopsApplication: insert_bops_applications_one(
object: {
bops_id: $bops_id
destination_url: $destination_url
request: $request
req_headers: $req_headers
response: $response
response_headers: $response_headers
session_id: $session_id
}
`,
{
bops_id: res.data.id,
destination_url: target,
request: exportData,
response: res.data,
response_headers: res.headers,
session_id: payload?.sessionId,
},
);

return {
application: {
...applicationId.insertBopsApplication,
bopsResponse: res.data,
},
};
})
.catch((error) => {
if (error.response) {
throw new Error(
`Sending to BOPS v2 failed (${[localAuthority, payload?.sessionId]
.filter(Boolean)
.join(" - ")}):\n${JSON.stringify(error.response.data, null, 2)}`,
);
} else {
// re-throw other errors
throw new Error(
`Sending to BOPS v2 failed (${[localAuthority, payload?.sessionId]
.filter(Boolean)
.join(" - ")}):\n${error}`,
);
) {
id
bopsId: bops_id
}
}
});
res.send(bopsResponse);
} catch (err) {
next(
`,
{
bops_id: response.data.id,
destination_url: response.request.url,
request: response.request.data,
response: response.data,
response_headers: response.headers,
session_id: payload?.sessionId,
},
);
res.send({
application: {
...applicationId.insertBopsApplication,
bopsResponse: response.data,
},
});
} catch (error) {
return next(
new ServerError({
status: 500,
message: `Sending to BOPS v2 failed (${[
localAuthority,
payload?.sessionId,
]
.filter(Boolean)
.join(" - ")})`,
cause: err,
.join(" - ")}).
${error}`,
cause: error,
}),
);
}
Expand Down
Loading

0 comments on commit cd6800e

Please sign in to comment.