Skip to content

Commit

Permalink
feat: check if changes have been made before publishing a new flow (#574
Browse files Browse the repository at this point in the history
)

* check if changes have been made before publishing a new flow

* feedback in the editor

* trying to mock passport

* test with mocked jwt

* more specific test

* finish basic tests
  • Loading branch information
sarahscott authored Aug 13, 2021
1 parent 3f22ace commit c26349a
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 56 deletions.
1 change: 0 additions & 1 deletion api.planx.uk/gis/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const makeEsriUrl = (domain, id, serverIndex = 0, overrideParams = {}) => {
.map((key) => key + "=" + escape(params[key]))
.join("&"),
].join("?");
console.log({ url });

return url;
};
Expand Down
2 changes: 0 additions & 2 deletions api.planx.uk/gis/local_authorities/southwark.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ async function locationSearch(x, y, extras) {
: false,
};

console.log(responses);

responses
.filter(([_key, result]) => result instanceof Error)
.forEach(([key, _result]) => {
Expand Down
23 changes: 1 addition & 22 deletions api.planx.uk/jest.setup.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
const { QueryMock } = require("graphql-query-test-mock");

require("dotenv").config({ path: "./.env.test" });

const queryMock = new QueryMock();
const { queryMock } = require("./tests/graphqlQueryMock");

beforeEach(() => {
queryMock.setup(process.env.HASURA_GRAPHQL_URL);

queryMock.mockQuery({
name: "GetTeams",
data: {
teams: [{ id: 1 }],
},
});

queryMock.mockQuery({
name: "CreateApplication",
data: {
insert_bops_applications_one: { id: 22 },
},
matchOnVariables: false,
variables: {
destination_url:
"https://southwark.bops.services/api/v1/planning_applications",
},
});
});
1 change: 1 addition & 0 deletions api.planx.uk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"http-proxy-middleware": "^1.3.1",
"https": "^1.0.0",
"isomorphic-fetch": "^2.2.1",
"jsondiffpatch": "^0.4.1",
"jsonwebtoken": "^8.5.1",
"mime": "^2.4.6",
"nanoid": "^3.1.12",
Expand Down
99 changes: 70 additions & 29 deletions api.planx.uk/publish.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { GraphQLClient } = require("graphql-request");
const jsondiffpatch = require("jsondiffpatch");

const client = new GraphQLClient(process.env.HASURA_GRAPHQL_URL, {
headers: {
Expand All @@ -21,6 +22,26 @@ const getFlowData = async (id) => {
return data.flows_by_pk.data;
};

const getMostRecentPublishedFlow = async (id) => {
const data = await client.request(
`
query GetMostRecentPublishedFlow($id: uuid!) {
flows_by_pk(id: $id) {
published_flows(limit: 1, order_by: { id: desc }) {
data
}
}
}
`,
{ id }
);

return (
data.flows_by_pk.published_flows[0] &&
data.flows_by_pk.published_flows[0].data
);
};

// XXX: getFlowData & dataMerged are currently repeated in ../editor.planx.uk/src/lib/dataMergedHotfix.ts
// in order to load previews for flows that have not been published yet
const dataMerged = async (id, ob = {}) => {
Expand Down Expand Up @@ -50,38 +71,58 @@ const dataMerged = async (id, ob = {}) => {
const publishFlow = async (req, res) => {
if (!req.user?.sub)
return res.status(401).json({ error: "User ID missing from JWT" });

const flattenedFlow = await dataMerged(req.params.flowId);

const publishedFlow = await client.request(
`
mutation PublishFlow(
$data: jsonb = {},
$flow_id: uuid,
$publisher_id: Int,
) {
insert_published_flows_one(object: {
data: $data,
flow_id: $flow_id,
publisher_id: $publisher_id,
}) {
id
flow_id
publisher_id
created_at
data
try {
const flattenedFlow = await dataMerged(req.params.flowId);
const mostRecent = await getMostRecentPublishedFlow(req.params.flowId);

const delta = jsondiffpatch.diff(mostRecent, flattenedFlow);

if (delta) {
const response = await client.request(
`
mutation PublishFlow(
$data: jsonb = {},
$flow_id: uuid,
$publisher_id: Int,
) {
insert_published_flows_one(object: {
data: $data,
flow_id: $flow_id,
publisher_id: $publisher_id,
}) {
id
flow_id
publisher_id
created_at
data
}
}
`,
{
data: flattenedFlow,
flow_id: req.params.flowId,
publisher_id: parseInt(req.user.sub, 10),
}
}`,
{
data: flattenedFlow,
flow_id: req.params.flowId,
publisher_id: parseInt(req.user.sub, 10),
}
);
);
const publishedFlow =
response.insert_published_flows_one &&
response.insert_published_flows_one.data;

try {
// return published flow record
res.json(publishedFlow.insert_published_flows_one);
const alteredNodes = Object.keys(delta).map((key) => ({
id: key,
...publishedFlow[key],
}));

res.json({
alteredNodes,
});
} else {
res.json({
alteredNodes: null,
message: "No new changes",
});
}
} catch (error) {
console.error(error);
res.status(500).json({ error });
Expand Down
217 changes: 217 additions & 0 deletions api.planx.uk/publish.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
const supertest = require("supertest");

const { queryMock } = require("./tests/graphqlQueryMock");
const { authHeader } = require("./tests/mockJWT");
const app = require("./server");

beforeEach(() => {
queryMock.mockQuery({
name: "GetFlowData",
matchOnVariables: false,
data: {
flows_by_pk: {
data: mockFlowData,
},
},
});

queryMock.mockQuery({
name: "GetMostRecentPublishedFlow",
matchOnVariables: false,
data: {
flows_by_pk: {
published_flows: [
{
data: mockFlowData,
},
],
},
},
});

queryMock.mockQuery({
name: "PublishFlow",
matchOnVariables: false,
data: {
insert_published_flows_one: {
data: mockFlowData,
},
},
});
});

it("does not update if there are no new changes", async () => {
await supertest(app)
.post("/flows/1/publish")
.set(authHeader())
.expect(200)
.then((res) => {
expect(res.body).toEqual({
alteredNodes: null,
message: "No new changes",
});
});
});

it("updates published flow and returns altered nodes if there have been changes", async () => {
const alteredFlow = {
...mockFlowData,
"4CJgXe8Ttl": {
data: {
flagSet: "Planning permission",
overrides: {
NO_APP_REQUIRED: {
heading: "Some Other Heading",
},
},
},
type: 3,
},
};

queryMock.mockQuery({
name: "GetFlowData",
matchOnVariables: false,
data: {
flows_by_pk: {
data: alteredFlow,
},
},
});

queryMock.mockQuery({
name: "PublishFlow",
matchOnVariables: false,
data: {
insert_published_flows_one: {
data: alteredFlow,
},
},
});

await supertest(app)
.post("/flows/1/publish")
.set(authHeader())
.expect(200)
.then((res) => {
expect(res.body).toEqual({
alteredNodes: [
{
id: "4CJgXe8Ttl",
type: 3,
data: {
flagSet: "Planning permission",
overrides: {
NO_APP_REQUIRED: {
heading: "Some Other Heading",
},
},
},
},
],
});
});
});

const mockFlowData = {
_root: {
edges: [
"RYYckLE2cH",
"R99ncwKifm",
"3qssvGXmMO",
"SEp0QeNsTS",
"q8Foul9hRN",
"4CJgXe8Ttl",
"dnVqd6zt4N",
],
},
"3qssvGXmMO": {
type: 9,
},
"4CJgXe8Ttl": {
data: {
flagSet: "Planning permission",
overrides: {
NO_APP_REQUIRED: {
heading: "Congratulations!",
},
},
},
type: 3,
},
"5sWfsvXphd": {
data: {
text: "?",
},
type: 200,
},
BV2VJhOC0I: {
data: {
text: "internal question",
},
type: 100,
edges: ["ScjaYmpbVK", "b7j9tq22dj"],
},
OL9JENldcI: {
data: {
text: "!!",
},
type: 200,
},
R99ncwKifm: {
data: {
text: "portal",
},
type: 300,
edges: ["BV2VJhOC0I"],
},
RYYckLE2cH: {
data: {
text: "Question",
},
type: 100,
edges: ["5sWfsvXphd", "OL9JENldcI"],
},
SEp0QeNsTS: {
data: {
fn: "application.fee.payable",
url: "http://localhost:7002/pay",
color: "#EFEFEF",
title: "Pay for your application",
description:
'<p>The planning fee covers the cost of processing your application. Find out more about how planning fees are calculated <a href="https://www.gov.uk/guidance/fees-for-planning-applications" target="_self">here</a>.</p>',
},
type: 400,
},
ScjaYmpbVK: {
data: {
text: "?",
},
type: 200,
},
b7j9tq22dj: {
data: {
text: "*",
},
type: 200,
},
dnVqd6zt4N: {
data: {
heading: "Application sent",
moreInfo:
"<h2>You will be contacted</h2>\n<ul>\n<li>if there is anything missing from the information you have provided so far</li>\n<li>if any additional information is required</li>\n<li>to arrange a site visit, if required</li>\n<li>to inform you whether a certificate has been granted or not</li>\n</ul>\n",
contactInfo:
'<p>You can contact us at <a href="mailto:planning@lambeth.gov.uk" target="_self"><strong>planning@lambeth.gov.uk</strong></a></p>\n',
description:
"A payment receipt has been emailed to you. You will also receive an email to confirm when your application has been received.",
feedbackCTA: "What did you think of this service? (takes 30 seconds)",
},
type: 725,
},
q8Foul9hRN: {
data: {
url: "http://localhost:7002/bops/southwark",
},
type: 650,
},
};
Loading

0 comments on commit c26349a

Please sign in to comment.