-
Notifications
You must be signed in to change notification settings - Fork 0
Fls 1450 designer pre award integration #221
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
base: main
Are you sure you want to change the base?
Changes from all commits
100bed3
611a345
10c46a1
8e254c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import config from "../config"; | ||
import * as Wreck from "@hapi/wreck"; | ||
|
||
interface FormJson { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My instinct would be that this isn't needed - best to just treat the form JSON as a black box, a bit like how the Pre-Award API itself does - let's just build a barren interface and let the downstream Designer internals figure out what to do with this object |
||
startPage: string; | ||
pages: any[]; | ||
sections: any[]; | ||
name: string; | ||
version?: number; | ||
conditions?: any[]; | ||
lists?: any[]; | ||
metadata?: any; | ||
fees?: any[]; | ||
outputs?: any[]; | ||
skipSummary?: boolean; | ||
[key: string]: any; | ||
} | ||
|
||
export interface FormData { | ||
name: string; | ||
form_json: FormJson; | ||
} | ||
|
||
export interface FormResponse { | ||
id: string; | ||
name: string; | ||
created_at: string; | ||
updated_at: string; | ||
published_at: string | null; | ||
draft_json: FormJson; | ||
published_json: FormJson; | ||
is_published: boolean; | ||
} | ||
|
||
export class PreAwardApiClient { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed you didn't include any auth headers in any of the requests to the Pre-Award Forms API, and initially I was confused why that wasn't breaking things - I thought I'd added auth protection to the API endpoints. This led to some digging, and it turns out that regardless of the fact that adding |
||
private baseUrl: string; | ||
|
||
constructor() { | ||
this.baseUrl = config.preAwardApiUrl; | ||
} | ||
|
||
async createOrUpdateForm(formData: FormData): Promise<FormResponse>{ | ||
const payload = formData; | ||
|
||
try{ | ||
const { payload: responseData } = await Wreck.post( | ||
`${this.baseUrl}/forms`, | ||
{ | ||
payload: JSON.stringify(payload), | ||
headers: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use the |
||
'Content-Type': 'application/json' | ||
} | ||
} | ||
); | ||
const parsedData = JSON.parse((responseData as Buffer).toString()); | ||
return parsedData as FormResponse; | ||
} | ||
catch (error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Catching an error just to throw it again with no side effects is a redundant behaviour, we might as well just remove the |
||
throw error; | ||
} | ||
} | ||
|
||
async getAllForms(): Promise<FormResponse[]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This takes ABSOLUTELY AGES if you have lots of forms. I've got 338 forms in my local Pre-Award database and I think I was waiting 7 or 8 seconds for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separate thing - but I noticed some weird behaviour whereby forms get repositioned in the existing forms list after you've edited them. Would be good to avoid that, as it makes you feel you've gone a bit mad as a user |
||
try { | ||
const { payload: responseData } = await Wreck.get( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually get an error running this code locally:
It works for me. I guess it's the same with |
||
`${this.baseUrl}/forms`, | ||
{ | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
} | ||
); | ||
|
||
const parsedData = JSON.parse((responseData as Buffer).toString()); | ||
return parsedData as FormResponse[]; | ||
} catch (error) { | ||
throw error; | ||
} | ||
} | ||
|
||
async getFormDraft(name: string): Promise<FormJson>{ | ||
try{ | ||
const { payload: responseData } = await Wreck.get( | ||
`${this.baseUrl}/forms/${name}/draft`, | ||
{ | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
} | ||
); | ||
const parsedData = JSON.parse((responseData as Buffer).toString()); | ||
return parsedData as FormJson; | ||
} | ||
catch (error) { | ||
throw error; | ||
} | ||
} | ||
} | ||
|
||
export const preAwardApiClient = new PreAwardApiClient(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { api as originalApi } from "../../../../digital-form-builder/designer/server/plugins/routes"; | ||
import { preAwardApiClient } from "../../lib/preAwardApiClient"; | ||
import config from "../../config"; | ||
import { ServerRoute, ResponseObject } from "@hapi/hapi"; | ||
|
||
// Extend the original getFormWithId with Pre-Award API support | ||
export const getFormWithId: ServerRoute = { | ||
...originalApi.getFormWithId, | ||
options: { | ||
...originalApi.getFormWithId.options || {}, | ||
handler: async (request, h) => { | ||
const { id } = request.params; | ||
const formJson = await preAwardApiClient.getFormDraft(id); | ||
return h.response(formJson).type("application/json"); | ||
}, | ||
}, | ||
}; | ||
|
||
// Extend the original putFormWithId with Pre-Award API support | ||
export const putFormWithId: ServerRoute = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function isn't actually used, believe it or not! It's overwritten by the |
||
...originalApi.putFormWithId, | ||
options: { | ||
...originalApi.putFormWithId.options || {}, | ||
handler: async (request, h) => { | ||
const { id } = request.params; | ||
const { Schema } = await import("../../../../digital-form-builder/model/src"); | ||
const { value, error } = Schema.validate(request.payload, { | ||
abortEarly: false, | ||
}); | ||
|
||
if (error) { | ||
throw new Error("Schema validation failed, reason: " + error.message); | ||
} | ||
const formData = { name: id, form_json: value }; | ||
await preAwardApiClient.createOrUpdateForm(formData); | ||
|
||
return h.response({ ok: true }).code(204); | ||
}, | ||
}, | ||
}; | ||
|
||
// Extend the original getAllPersistedConfigurations with Pre-Award API support | ||
export const getAllPersistedConfigurations: ServerRoute = { | ||
...originalApi.getAllPersistedConfigurations, | ||
options: { | ||
...originalApi.getAllPersistedConfigurations.options || {}, | ||
handler: async (request, h): Promise<ResponseObject | undefined> => { | ||
const forms = await preAwardApiClient.getAllForms(); | ||
const response = forms.map(form => ({ | ||
Key: form.name, | ||
DisplayName: form.name, | ||
LastModified: form.updated_at | ||
})); | ||
return h.response(response).type("application/json"); | ||
}, | ||
}, | ||
}; | ||
|
||
// Use original log route as-is | ||
export const log = originalApi.log; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { newConfig as originalNewConfig } from "../../../../digital-form-builder/designer/server/plugins/routes"; | ||
import { preAwardApiClient } from "../../lib/preAwardApiClient"; | ||
import config from "../../config"; | ||
import { ServerRoute } from "@hapi/hapi"; | ||
import { HapiRequest } from "../../../../digital-form-builder/designer/server/types"; | ||
import { nanoid } from "nanoid"; | ||
import newFormJson from "../../../../digital-form-builder/designer/new-form.json"; | ||
|
||
// Extend the original registerNewFormWithRunner with Pre-Award API support | ||
export const registerNewFormWithRunner: ServerRoute = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need both this and |
||
...originalNewConfig.registerNewFormWithRunner, | ||
options: { | ||
...originalNewConfig.registerNewFormWithRunner.options, | ||
handler: async (request: HapiRequest, h) => { | ||
const { selected, name } = request.payload as any; | ||
|
||
if (name && name !== "" && !name.match(/^[a-zA-Z0-9 _-]+$/)) { | ||
return h | ||
.response("Form name should not contain special characters") | ||
.type("application/json") | ||
.code(400); | ||
} | ||
|
||
const newName = name === "" ? nanoid(10) : name; | ||
|
||
if (selected.Key === "New") { | ||
const formData = { name: newName, form_json: newFormJson }; | ||
await preAwardApiClient.createOrUpdateForm(formData); | ||
} else { | ||
const existingForm = await preAwardApiClient.getFormDraft(selected.Key); | ||
const formData = { name: newName, form_json: existingForm }; | ||
await preAwardApiClient.createOrUpdateForm(formData); | ||
} | ||
|
||
const response = JSON.stringify({ | ||
id: `${newName}`, | ||
previewUrl: config.previewUrl, | ||
}); | ||
return h.response(response).type("application/json").code(200); | ||
}, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const Babel = require("@babel/core"); | ||
|
||
module.exports = [ | ||
{ | ||
ext: ".ts", | ||
transform: (content, filename) => { | ||
const result = Babel.transformSync(content, { | ||
filename, | ||
presets: [ | ||
["@babel/preset-env", { targets: { node: "current" } }], | ||
"@babel/preset-typescript" | ||
] | ||
}); | ||
return result.code; | ||
} | ||
} | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see the path
/forms
added to thepreAwardApiUrl
throughout - this path is part of all URLs we hit, and so we should share it for simplicity, plus it's the way I've done it in Runner, so it'll be consistent