Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production deploy #2377

Merged
merged 21 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f064283
fix: React errors on content component (#2350)
DafyddLlyr Oct 27, 2023
6693053
chore: Fix Run API test type warnings (#2343)
DafyddLlyr Oct 27, 2023
c97f0f4
feat: first pass reordering uploaded files to show most recent first …
Mike-Heneghan Oct 27, 2023
a9db181
feat: Capture user agent and referrer analytics (#2349)
DafyddLlyr Oct 31, 2023
723a0e7
fix: remove reset logic which cleared all slots (#2353)
Mike-Heneghan Oct 31, 2023
51c1b32
feat: track a user hitting the "Save" application link (#2355)
Mike-Heneghan Oct 31, 2023
7d49452
chore: bump `planx-core` (#2352)
jessicamcinchak Nov 1, 2023
3474a45
fix: Improve flakey E2E tests (#2351)
DafyddLlyr Nov 1, 2023
bfc5b81
chore(deps): bump actions/setup-node from 3 to 4 (#2359)
dependabot[bot] Nov 1, 2023
e33ca4b
chore(deps-dev): bump eslint-plugin-jest in /api.planx.uk (#2360)
dependabot[bot] Nov 1, 2023
2b7604e
chore(deps-dev): bump lint-staged from 14.0.0 to 15.0.2 in /api.planx…
dependabot[bot] Nov 1, 2023
35eceec
chore(deps-dev): bump @babel/preset-typescript in /api.planx.uk (#2363)
dependabot[bot] Nov 1, 2023
05e423a
chore(deps-dev): bump @storybook/react in /editor.planx.uk (#2366)
dependabot[bot] Nov 1, 2023
a3b0bf2
chore(deps-dev): bump node-dev from 7.4.3 to 8.0.0 in /api.planx.uk (…
dependabot[bot] Nov 1, 2023
151368f
chore(deps): bump axios from 1.4.0 to 1.6.0 in /api.planx.uk (#2361)
dependabot[bot] Nov 1, 2023
908dee0
[skip pizza] bump lint-staged from 14.0.1 to 15.0.2 in /e2e (#2371)
dependabot[bot] Nov 1, 2023
8a1a0ca
[skip pizza] bump eslint from 8.50.0 to 8.52.0 in /e2e (#2372)
dependabot[bot] Nov 1, 2023
4e29137
chore: Bump planx-core (#2373)
DafyddLlyr Nov 2, 2023
cde4cd5
fix: Don't overwrite `req` with `parsedReq` (#2354)
DafyddLlyr Nov 2, 2023
115259b
chore: Bump Axios (#2374)
DafyddLlyr Nov 3, 2023
8e9f01a
chore: enable Barnet to query Planning Constraints (#2376)
jessicamcinchak Nov 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -125,7 +125,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -161,7 +161,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -192,7 +192,7 @@ jobs:
if: steps.cache-react-build-assets.outputs.cache-hit != 'true'
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
if: steps.cache-react-build-assets.outputs.cache-hit != 'true'
with:
node-version: ${{ vars.NODE_VERSION }}
Expand Down Expand Up @@ -246,7 +246,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -447,7 +447,7 @@ jobs:
with:
version: ${{ vars.PNPM_VERSION }}
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/push-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
# https://docs.github.com/en/free-pro-team@latest/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action
Expand Down Expand Up @@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
- name: Configure AWS Credentials
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/push-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
# https://docs.github.com/en/free-pro-team@latest/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action
Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
- name: Configure AWS Credentials
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/regression-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
- uses: pnpm/[email protected]
with:
version: ${{ vars.PNPM_VERSION }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down Expand Up @@ -141,7 +141,7 @@ jobs:
with:
version: ${{ vars.PNPM_VERSION }}
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ vars.NODE_VERSION }}
cache: "pnpm"
Expand Down
17 changes: 9 additions & 8 deletions api.planx.uk/editor/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import supertest from "supertest";
import { queryMock } from "../tests/graphqlQueryMock";
import { authHeader } from "../tests/mockJWT";
import app from "../server";
import { Flow } from "../types";
import { flowWithInviteToPay } from "../tests/mocks/inviteToPayData";
import { FlowGraph } from "@opensystemslab/planx-core/types";

beforeEach(() => {
queryMock.mockQuery({
Expand Down Expand Up @@ -168,7 +168,7 @@ describe("sections validation on diff", () => {
});

it("does not update if there are sections, but there is not a section in the first position", async () => {
const flowWithSections: Flow["data"] = {
const flowWithSections: FlowGraph = {
_root: {
edges: ["questionNode", "sectionNode"],
},
Expand Down Expand Up @@ -205,7 +205,7 @@ describe("sections validation on diff", () => {

describe("invite to pay validation on diff", () => {
it("does not update if invite to pay is enabled, but there is not a Send component", async () => {
const { Send, ...invalidatedFlow } = flowWithInviteToPay;
const { Send: _Send, ...invalidatedFlow } = flowWithInviteToPay;
invalidatedFlow["_root"].edges?.splice(
invalidatedFlow["_root"].edges?.indexOf("Send"),
);
Expand Down Expand Up @@ -266,7 +266,8 @@ describe("invite to pay validation on diff", () => {
});

it("does not update if invite to pay is enabled, but there is not a FindProperty (`_address`) component", async () => {
const { FindProperty, ...invalidatedFlow } = flowWithInviteToPay;
const { FindProperty: _FindProperty, ...invalidatedFlow } =
flowWithInviteToPay;
invalidatedFlow["_root"].edges?.splice(
invalidatedFlow["_root"].edges?.indexOf("FindProperty"),
);
Expand Down Expand Up @@ -326,9 +327,9 @@ describe("invite to pay validation on diff", () => {

it("does not update if invite to pay is enabled, but there is not a Checklist that sets `proposal.projectType`", async () => {
const {
Checklist,
ChecklistOptionOne,
ChecklistOptionTwo,
Checklist: _Checklist,
ChecklistOptionOne: _ChecklistOptionOne,
ChecklistOptionTwo: _ChecklistOptionTwo,
...invalidatedFlow
} = flowWithInviteToPay;
invalidatedFlow["_root"].edges?.splice(
Expand Down Expand Up @@ -358,7 +359,7 @@ describe("invite to pay validation on diff", () => {
});
});

const mockFlowData: Flow["data"] = {
const mockFlowData: FlowGraph = {
_root: {
edges: [
"SectionOne",
Expand Down
91 changes: 57 additions & 34 deletions api.planx.uk/editor/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import { adminGraphQLClient as adminClient } from "../hasura";
import { dataMerged, getMostRecentPublishedFlow } from "../helpers";
import { gql } from "graphql-request";
import intersection from "lodash/intersection";
import { ComponentType } from "@opensystemslab/planx-core/types";
import {
ComponentType,
FlowGraph,
Node,
} from "@opensystemslab/planx-core/types";
import { userContext } from "../modules/auth/middleware";
import type { Entry } from "type-fest";

const validateAndDiffFlow = async (
req: Request,
Expand Down Expand Up @@ -139,17 +144,17 @@ type ValidationResponse = {
description?: string;
};

const validateSections = (flow: Record<string, any>): ValidationResponse => {
if (getSectionNodeIds(flow)?.length > 0) {
if (!sectionIsInFirstPosition(flow)) {
const validateSections = (flowGraph: FlowGraph): ValidationResponse => {
if (getSectionNodeIds(flowGraph)?.length > 0) {
if (!sectionIsInFirstPosition(flowGraph)) {
return {
isValid: false,
message: "Cannot publish an invalid flow",
description: "When using Sections, your flow must start with a Section",
};
}

if (!allSectionsOnRoot(flow)) {
if (!allSectionsOnRoot(flowGraph)) {
return {
isValid: false,
message: "Cannot publish an invalid flow",
Expand All @@ -165,57 +170,58 @@ const validateSections = (flow: Record<string, any>): ValidationResponse => {
};
};

const getSectionNodeIds = (flow: Record<string, any>): string[] => {
return Object.entries(flow)
.filter(([_nodeId, nodeData]) => nodeData?.type === ComponentType.Section)
?.map(([nodeId, _nodeData]) => nodeId);
const getSectionNodeIds = (flowGraph: FlowGraph): string[] => {
const sectionNodes = Object.entries(flowGraph).filter((entry) =>
isComponentType(entry, ComponentType.Section),
);
return sectionNodes.map(([nodeId, _nodeData]) => nodeId);
};

const sectionIsInFirstPosition = (flow: Record<string, any>): boolean => {
const firstNodeId = flow["_root"].edges[0];
return flow[firstNodeId].type === ComponentType.Section;
const sectionIsInFirstPosition = (flowGraph: FlowGraph): boolean => {
const firstNodeId = flowGraph["_root"].edges[0];
return flowGraph[firstNodeId].type === ComponentType.Section;
};

const allSectionsOnRoot = (flow: Record<string, any>): boolean => {
const sectionTypeNodeIds = getSectionNodeIds(flow);
const allSectionsOnRoot = (flowData: FlowGraph): boolean => {
const sectionTypeNodeIds = getSectionNodeIds(flowData);
const intersectingNodeIds = intersection(
flow["_root"].edges,
flowData["_root"].edges,
sectionTypeNodeIds,
);
return intersectingNodeIds.length === sectionTypeNodeIds.length;
};

const validateInviteToPay = (flow: Record<string, any>): ValidationResponse => {
const validateInviteToPay = (flowGraph: FlowGraph): ValidationResponse => {
const invalidResponseTemplate = {
isValid: false,
message: "Cannot publish an invalid flow",
};

if (inviteToPayEnabled(flow)) {
if (numberOfComponentType(flow, ComponentType.Pay) > 1) {
if (inviteToPayEnabled(flowGraph)) {
if (numberOfComponentType(flowGraph, ComponentType.Pay) > 1) {
return {
...invalidResponseTemplate,
description:
"When using Invite to Pay, your flow must have exactly ONE Pay",
};
}

if (!hasComponentType(flow, ComponentType.Send)) {
if (!hasComponentType(flowGraph, ComponentType.Send)) {
return {
...invalidResponseTemplate,
description: "When using Invite to Pay, your flow must have a Send",
};
}

if (numberOfComponentType(flow, ComponentType.Send) > 1) {
if (numberOfComponentType(flowGraph, ComponentType.Send) > 1) {
return {
...invalidResponseTemplate,
description:
"When using Invite to Pay, your flow must have exactly ONE Send. It can select many destinations",
};
}

if (!hasComponentType(flow, ComponentType.FindProperty)) {
if (!hasComponentType(flowGraph, ComponentType.FindProperty)) {
return {
...invalidResponseTemplate,
description:
Expand All @@ -224,7 +230,11 @@ const validateInviteToPay = (flow: Record<string, any>): ValidationResponse => {
}

if (
!hasComponentType(flow, ComponentType.Checklist, "proposal.projectType")
!hasComponentType(
flowGraph,
ComponentType.Checklist,
"proposal.projectType",
)
) {
return {
...invalidResponseTemplate,
Expand All @@ -241,27 +251,40 @@ const validateInviteToPay = (flow: Record<string, any>): ValidationResponse => {
};
};

const inviteToPayEnabled = (flow: Record<string, any>): boolean => {
const payNodeStatuses = Object.entries(flow)
.filter(([_nodeId, nodeData]) => nodeData?.type === ComponentType.Pay)
?.map(([_nodeId, nodeData]) => nodeData?.data?.allowInviteToPay);
const inviteToPayEnabled = (flowGraph: FlowGraph): boolean => {
const payNodes = Object.entries(flowGraph).filter(
(entry): entry is [string, Node] =>
isComponentType(entry, ComponentType.Pay),
);
const payNodeStatuses = payNodes.map(
([_nodeId, node]) => node?.data?.allowInviteToPay,
);
return (
payNodeStatuses.length > 0 &&
payNodeStatuses.every((status) => status === true)
);
};

const isComponentType = (
entry: Entry<FlowGraph>,
type: ComponentType,
): entry is [string, Node] => {
const [nodeId, node] = entry;
if (nodeId === "_root") return false;
return Boolean(node?.type === type);
};

const hasComponentType = (
flow: Record<string, any>,
flowGraph: FlowGraph,
type: ComponentType,
fn?: string,
): boolean => {
const nodeIds = Object.entries(flow).filter(
([_nodeId, nodeData]) => nodeData?.type === type,
const nodeIds = Object.entries(flowGraph).filter(
(entry): entry is [string, Node] => isComponentType(entry, type),
);
if (fn) {
nodeIds
?.filter(([_nodeId, nodeData]) => nodeData?.data.fn === fn)
?.filter(([_nodeId, nodeData]) => nodeData?.data?.fn === fn)
?.map(([nodeId, _nodeData]) => nodeId);
} else {
nodeIds?.map(([nodeId, _nodeData]) => nodeId);
Expand All @@ -270,16 +293,16 @@ const hasComponentType = (
};

const numberOfComponentType = (
flow: Record<string, any>,
flowGraph: FlowGraph,
type: ComponentType,
fn?: string,
): number => {
const nodeIds = Object.entries(flow).filter(
([_nodeId, nodeData]) => nodeData?.type === type,
const nodeIds = Object.entries(flowGraph).filter(
(entry): entry is [string, Node] => isComponentType(entry, type),
);
if (fn) {
nodeIds
?.filter(([_nodeId, nodeData]) => nodeData?.data.fn === fn)
?.filter(([_nodeId, nodeData]) => nodeData?.data?.fn === fn)
?.map(([nodeId, _nodeData]) => nodeId);
} else {
nodeIds?.map(([nodeId, _nodeData]) => nodeId);
Expand Down
2 changes: 1 addition & 1 deletion api.planx.uk/gis/classifiedRoads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type OSFeatures = {
type: "Feature";
geometry: {
type: string;
coordinates: any[];
coordinates: number[];
};
properties: OSHighwayFeature;
}[];
Expand Down
Loading
Loading