Skip to content

Commit

Permalink
refactor: Make redaction explicit in ExportClient (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr authored Oct 16, 2023
1 parent 7ca7704 commit 3a85966
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 154 deletions.
23 changes: 0 additions & 23 deletions src/export/csv/index.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
import omit from "lodash.omit";

import type {
BOPSExportData,
BOPSFullPayload,
ExportData,
Passport,
QuestionAndResponses,
Response,
} from "../../types";

export function computeCSVData({
sessionId,
bopsExportData,
passport,
}: {
sessionId: string;
bopsExportData: BOPSExportData;
passport: Passport;
}): ExportData {
const compute = (input: BOPSFullPayload) =>
computeQuestionAndResponses({
sessionId,
bopsData: input,
passport,
});
return {
responses: compute(bopsExportData.exportData),
redactedResponses: compute(bopsExportData.redactedExportData),
};
}

export function computeQuestionAndResponses({
sessionId,
bopsData,
passport,
Expand Down
11 changes: 5 additions & 6 deletions src/export/digitalPlanning/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { GraphQLClient } from "graphql-request";

import { Passport } from "../../models/passport";
import { getSessionById } from "../../requests/session";
import { ExportParams } from "..";
import { DigitalPlanning } from "./model";
import { DigitalPlanningDataSchema as DigitalPlanningPayload } from "./schema/types";

export async function generateDigitalPlanningPayload(
client: GraphQLClient,
sessionId: string,
): Promise<DigitalPlanningPayload> {
export async function generateDigitalPlanningPayload({
client,
sessionId,
}: ExportParams): Promise<DigitalPlanningPayload> {
const session = await getSessionById(client, sessionId);
if (!session) throw Error(`No session found matching ID ${sessionId}`);

Expand Down
143 changes: 142 additions & 1 deletion src/export/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,142 @@
export * from "./oneApp";
import { GraphQLClient } from "graphql-request";

import { findPublishedFlowBySessionId, getFlowName } from "../requests/flow";
import { getSessionById, getSessionPassport } from "../requests/session";
import type { BOPSFullPayload, QuestionAndResponses } from "../types";
import { computeBOPSParams } from "./bops";
import { computeCSVData } from "./csv";
import { generateDigitalPlanningPayload } from "./digitalPlanning";
import { DigitalPlanningDataSchema } from "./digitalPlanning/schema/types";
import { generateOneAppXML } from "./oneApp";

export type ExportParams = {
client: GraphQLClient;
sessionId: string;
};

type RedactionOptions =
| { isRedacted?: false; keysToRedact?: never }
| { isRedacted: true; keysToRedact?: string[] };

export type ExportWithRedactionParams = ExportParams & RedactionOptions;

export class ExportClient {
protected client: GraphQLClient;

constructor(client: GraphQLClient) {
this.client = client;
}

csvData(sessionId: string): Promise<QuestionAndResponses[]> {
return generateCSVData({ client: this.client, sessionId });
}

csvDataRedacted(sessionId: string): Promise<QuestionAndResponses[]> {
return generateCSVData({
client: this.client,
sessionId,
isRedacted: true,
});
}

bopsPayload(sessionId: string): Promise<BOPSFullPayload> {
return generateBOPSPayload({ client: this.client, sessionId });
}

bopsPayloadRedacted(sessionId: string): Promise<BOPSFullPayload> {
return generateBOPSPayload({
client: this.client,
sessionId,
isRedacted: true,
});
}

digitalPlanningDataPayload(
sessionId: string,
): Promise<DigitalPlanningDataSchema> {
return generateDigitalPlanningPayload({ client: this.client, sessionId });
}

async oneAppPayload(sessionId: string): Promise<string> {
return generateOneAppXML({ client: this.client, sessionId });
}
}

export async function generateCSVData({
client,
sessionId,
isRedacted,
}: ExportWithRedactionParams) {
const bopsData = await generateBOPSPayload({ client, sessionId, isRedacted });
if (!bopsData) {
throw new Error(
`Cannot fetch BOPS data for session ${sessionId} so cannot generate CSV Data`,
);
}

const passport = await getSessionPassport(client, sessionId);
if (!passport) {
throw new Error(
`Cannot find passport for session ${sessionId} so cannot generate CSV Data`,
);
}

return computeCSVData({
sessionId,
bopsData,
passport,
});
}

export async function generateBOPSPayload({
client,
sessionId,
isRedacted = false,
keysToRedact,
}: ExportWithRedactionParams): Promise<BOPSFullPayload> {
try {
const session = await getSessionById(client, sessionId);
if (!session) throw new Error(`Cannot find session ${sessionId}`);

const flow = await findPublishedFlowBySessionId(client, sessionId);
if (!flow) throw new Error(`Cannot get flow ${session.flowId}`);

const flowName = await getFlowName(client, session.flowId);
const { breadcrumbs, passport } = session.data;

if (isRedacted) {
// compute redacted export data
const defaultKeysToRedact = [
"applicant.phone.primary",
"applicant.phone.secondary",
"applicant.email",
"applicant.agent.phone.primary",
"applicant.agent.phone.secondary",
"applicant.agent.email",
];
const redactedExportData = computeBOPSParams({
sessionId,
flow,
flowName,
breadcrumbs,
passport,
keysToRedact: keysToRedact || defaultKeysToRedact,
});

return redactedExportData;
}

// compute export data
const exportData = computeBOPSParams({
sessionId,
flow,
flowName,
breadcrumbs,
passport,
});

return exportData;
} catch (e) {
throw new Error(`Cannot generate BOPS payload: ${e}`);
}
}
2 changes: 1 addition & 1 deletion src/export/oneApp/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe("generateOneAppXML", () => {
.mockImplementationOnce(() => false)
.mockImplementationOnce(() => false);

const xml = await generateOneAppXML(client, "abc123");
const xml = await generateOneAppXML({ client, sessionId: "abc123" });
expect(xml).not.toBeUndefined();
const isValid = XMLValidator.validate(xml!);
expect(isValid).toBe(true);
Expand Down
11 changes: 5 additions & 6 deletions src/export/oneApp/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { GraphQLClient } from "graphql-request";

import { Passport } from "../../models/passport";
import { getDocumentTemplateNamesForSession } from "../../requests/document-templates";
import { getSessionById } from "../../requests/session";
import { hasRequiredDataForTemplate } from "../../templates";
import { Passport as IPassport } from "../../types";
import { ExportParams } from "..";
import { OneAppPayload } from "./model";

export async function generateOneAppXML(
client: GraphQLClient,
sessionId: string,
): Promise<string> {
export async function generateOneAppXML({
client,
sessionId,
}: ExportParams): Promise<string> {
const session = await getSessionById(client, sessionId);
if (!session) throw Error(`No session found matching ID ${sessionId}`);

Expand Down
105 changes: 0 additions & 105 deletions src/requests/export.ts

This file was deleted.

7 changes: 1 addition & 6 deletions src/requests/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type { GraphQLClient } from "graphql-request";
import slugify from "lodash.kebabcase";

import { generateOneAppXML } from "../export/oneApp";
import { ExportClient } from "../export";
import type { KeyPath, PaymentRequest, Session } from "../types";
import { ApplicationClient } from "./application";
import {
getDocumentTemplateNamesForFlow,
getDocumentTemplateNamesForSession,
} from "./document-templates";
import { ExportClient } from "./export";
import { createFlow, FlowClient, publishFlow } from "./flow";
import { Auth, getGraphQLClient } from "./graphql";
import { createPaymentRequest, PaymentRequestClient } from "./payment-request";
Expand Down Expand Up @@ -111,10 +110,6 @@ export class CoreDomainClient {
return getDocumentTemplateNamesForSession(this.client, sessionId);
}

async generateOneAppXML(sessionId: string): Promise<string> {
return generateOneAppXML(this.client, sessionId);
}

async getSessionById(sessionId: string): Promise<Session | null> {
return getSessionById(this.client, sessionId);
}
Expand Down
7 changes: 1 addition & 6 deletions src/types/export.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import { BOPSFullPayload, QuestionAndResponses } from "./bops";

export type BOPSExportData = {
exportData: BOPSFullPayload;
redactedExportData: BOPSFullPayload;
};
import { QuestionAndResponses } from "./bops";

export type ExportData = {
responses: QuestionAndResponses[];
Expand Down

0 comments on commit 3a85966

Please sign in to comment.