Skip to content

Commit

Permalink
E2E data flow for submission form
Browse files Browse the repository at this point in the history
  • Loading branch information
kyle1morel committed Dec 12, 2023
1 parent cdb8b46 commit c2dde06
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 104 deletions.
2 changes: 1 addition & 1 deletion app/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"apiPath": "/api/v1",
"bodyLimit": "30mb",
"db": {
"database": "pns",
"database": "pcns",
"host": "localhost",
"port": "5432",
"poolMin": "2",
Expand Down
4 changes: 3 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
"migrate:up": "knex migrate:up",
"postmigrate:up": "npm run prisma:sync",
"seed": "knex seed:run",
"prisma:sync": "prisma db pull"
"prisma:sync": "prisma db pull",
"postprisma:sync": "npm run prisma:generate",
"prisma:generate": "prisma generate"
},
"dependencies": {
"@prisma/client": "^5.7.0",
Expand Down
22 changes: 22 additions & 0 deletions app/src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,25 @@ export function redactSecrets(data: { [key: string]: unknown }, fields: Array<st
}
return data;
}

/**
* @function fromYrn
* Converts a YRN string to boolean
* @param {string | null | undefined} yrn An arbitrary string
* @returns {boolean | null} The converted value
*/
export function fromYrn(yrn: string | null | undefined): boolean | null {
if (!yrn) return null;
return yrn.toUpperCase() === 'Y' ? true : false;
}

/**
* @function toYrn
* Converts a boolean value to a YRN string
* @param {boolean | null | undefined} bool An arbitrary boolean
* @returns {sring | null} The converted value
*/
export function toYrn(bool: boolean | null | undefined): string | null {
if (bool === null || bool === undefined) return null;
return bool ? 'Y' : 'N';
}
25 changes: 6 additions & 19 deletions app/src/controllers/chefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import { IdentityProvider } from '../components/constants';
import type { NextFunction, Request, Response } from 'express';
import type { JwtPayload } from 'jsonwebtoken';
import type { ChefsFormConfig, ChefsFormConfigData } from '../types/ChefsFormConfig';
import type { ChefsSubmissionDataSource } from '../types/ChefsSubmissionDataSource';
import type { ChefsSubmissionFormExport } from '../types/ChefsSubmissionFormExport';

const controller = {
getFormExport: async (req: Request, res: Response, next: NextFunction) => {
try {
const cfg = config.get('server.chefs.forms') as ChefsFormConfig;
let formData = new Array<ChefsSubmissionDataSource>();
let formData = new Array<ChefsSubmissionFormExport>();

// Get a JSON export of all form data merged into a single array
await Promise.all(
Object.values<ChefsFormConfigData>(cfg).map(async (x: ChefsFormConfigData) => {
const data = await chefsService.getFormExport(x.id);
data.forEach((d: ChefsSubmissionDataSource) => (d.form.id = x.id));
data.forEach((d: ChefsSubmissionFormExport) => (d.form.id = x.id));
formData = formData.concat(data);
})
);
Expand All @@ -29,7 +29,7 @@ const controller = {
* IDIR users should be able to see all submissions
* BCeID/Business should only see their own submissions
*/
const filterData = (data: Array<ChefsSubmissionDataSource>) => {
const filterData = (data: Array<ChefsSubmissionFormExport>) => {
const tokenPayload = req.currentUser?.tokenPayload as JwtPayload;
const filterToUser = tokenPayload && tokenPayload.identity_provider !== IdentityProvider.IDIR;

Expand All @@ -52,16 +52,7 @@ const controller = {

getSubmission: async (req: Request, res: Response, next: NextFunction) => {
try {
const response = await chefsService.getSubmission(req.query.formId as string, req.params.formSubmissionId);
res.status(200).send(response);
} catch (e: unknown) {
next(e);
}
},

getSubmissionStatus: async (req: Request, res: Response, next: NextFunction) => {
try {
const response = await chefsService.getSubmissionStatus(req.query.formId as string, req.params.formSubmissionId);
const response = await chefsService.getSubmission(req.query.formId as string, req.params.submissionId);
res.status(200).send(response);
} catch (e: unknown) {
next(e);
Expand All @@ -70,11 +61,7 @@ const controller = {

updateSubmission: async (req: Request, res: Response, next: NextFunction) => {
try {
const response = await chefsService.updateSubmission(
req.query.formId as string,
req.params.formSubmissionId,
req.body
);
const response = await chefsService.updateSubmission(req.body);
res.status(200).send(response);
} catch (e: unknown) {
next(e);
Expand Down
12 changes: 3 additions & 9 deletions app/src/db/migrations/20231212000000_init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,7 @@ export async function up(knex: Knex): Promise<void> {
.then(() =>
knex.schema.createTable('submission', (table) => {
table.uuid('submissionId').primary();
table
.uuid('assigneeUserId')
.references('userId')
.inTable('user')
.notNullable()
.onUpdate('CASCADE')
.onDelete('CASCADE');
table.uuid('assigneeToUserId').references('userId').inTable('user').onUpdate('CASCADE').onDelete('CASCADE');
table.string('confirmationId', 255);
table.string('contactEmail', 255);
table.string('contactPhoneNumber', 255);
Expand All @@ -64,8 +58,8 @@ export async function up(knex: Knex): Promise<void> {
table.string('relatedPermits', 255);
table.boolean('updatedAai');
table.string('waitingOn', 255);
table.timestamp('shasCreatedAt', { useTz: true });
table.string('shasCreatedBy', 255);
table.timestamp('submittedAt', { useTz: true });
table.string('submittedBy', 255);
table.timestamp('bringForwardDate', { useTz: true });
table.string('notes', 2047);
stamps(knex, table);
Expand Down
8 changes: 4 additions & 4 deletions app/src/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ model knex_migrations_lock {

model submission {
submissionId String @id @db.Uuid
assigneeUserId String @db.Uuid
assigneeToUserId String? @db.Uuid
confirmationId String? @db.VarChar(255)
contactEmail String? @db.VarChar(255)
contactPhoneNumber String? @db.VarChar(255)
Expand All @@ -49,15 +49,15 @@ model submission {
relatedPermits String? @db.VarChar(255)
updatedAai Boolean?
waitingOn String? @db.VarChar(255)
shasCreatedAt DateTime? @db.Timestamptz(6)
shasCreatedBy String? @db.VarChar(255)
submittedAt DateTime? @db.Timestamptz(6)
submittedBy String? @db.VarChar(255)
bringForwardDate DateTime? @db.Timestamptz(6)
notes String? @db.VarChar(2047)
createdBy String? @default("00000000-0000-0000-0000-000000000000") @db.VarChar(255)
createdAt DateTime? @default(now()) @db.Timestamptz(6)
updatedBy String? @db.VarChar(255)
updatedAt DateTime? @db.Timestamptz(6)
user user @relation(fields: [assigneeUserId], references: [userId], onDelete: Cascade, map: "submission_assigneeuserid_foreign")
user user? @relation(fields: [assigneeToUserId], references: [userId], onDelete: Cascade, map: "submission_assigneetouserid_foreign")
}

model user {
Expand Down
6 changes: 6 additions & 0 deletions app/src/interfaces/IStamps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface IStamps {
createdBy?: string;
createdAt?: string;
updatedBy?: string;
updatedAt?: string;
}
21 changes: 4 additions & 17 deletions app/src/routes/v1/chefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,16 @@ router.get('/export', (req: Request, res: Response, next: NextFunction): void =>

// Submission endpoint
router.get(
'/submission/:formSubmissionId',
'/submission/:submissionId',
requireChefsFormConfigData,
(req: Request, res: Response, next: NextFunction): void => {
chefsController.getSubmission(req, res, next);
}
);

// Submission endpoint
router.put(
'/submission/:formSubmissionId',
requireChefsFormConfigData,
(req: Request, res: Response, next: NextFunction): void => {
chefsController.updateSubmission(req, res, next);
}
);

// Submission status endpoint
router.get(
'/submission/:formSubmissionId/status',
requireChefsFormConfigData,
(req: Request, res: Response, next: NextFunction): void => {
chefsController.getSubmissionStatus(req, res, next);
}
);
router.put('/submission/:submissionId', (req: Request, res: Response, next: NextFunction): void => {
chefsController.updateSubmission(req, res, next);
});

export default router;
78 changes: 63 additions & 15 deletions app/src/services/chefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import axios from 'axios';
import config from 'config';
import { PrismaClient } from '@prisma/client';
import { NIL } from 'uuid';

import { getChefsApiKey } from '../components/utils';
import { fromYrn, getChefsApiKey, toYrn } from '../components/utils';

import type { AxiosInstance, AxiosRequestConfig } from 'axios';
import type { ChefsSubmissionForm } from '../types/ChefsSubmissionForm';

const prisma = new PrismaClient();

Expand Down Expand Up @@ -36,28 +38,74 @@ const service = {
}
},

getSubmission: async (formId: string, formSubmissionId: string) => {
getSubmission: async (formId: string, submissionId: string) => {
try {
const response = await chefsAxios(formId).get(`submissions/${formSubmissionId}`);
return response.data;
} catch (e: unknown) {
throw e;
}
},
// Try to pull data from our DB
let result = await prisma.submission.findUnique({
where: {
submissionId: submissionId
}
});

getSubmissionStatus: async (formId: string, formSubmissionId: string) => {
try {
const response = await chefsAxios(formId).get(`submissions/${formSubmissionId}/status`);
return response.data;
// Pull submission data from CHEFS and store to our DB if it doesn't exist
if (!result) {
const response = (await chefsAxios(formId).get(`submissions/${submissionId}`)).data;
const status = (await chefsAxios(formId).get(`submissions/${submissionId}/status`)).data;

// TODO: Assigned to correct user
result = await prisma.submission.create({
data: {
submissionId: response.submission.id,
assigneeToUserId: NIL, //status[0].assignedToUserId,
confirmationId: response.submission.confirmationId,
contactEmail: response.submission.submission.data.contactEmail,
contactPhoneNumber: response.submission.submission.data.contactPhoneNumber,
contactFirstName: response.submission.submission.data.contactFirstName,
contactLastName: response.submission.submission.data.contactLastName,
intakeStatus: status[0].code,
projectName: response.submission.submission.data.projectName,
queuePriority: response.submission.submission.data.queuePriority,
singleFamilyUnits: response.submission.submission.data.singleFamilyUnits,
streetAddress: response.submission.submission.data.streetAddress,
atsClientNumber: null,
addedToATS: null,
financiallySupported: null,
applicationStatus: null,
relatedPermits: null,
updatedAai: null,
waitingOn: null,
submittedAt: response.submission.createdAt,
submittedBy: response.submission.createdBy,
bringForwardDate: null,
notes: null
}
});
}

return {
...result,
addedToATS: toYrn(result.addedToATS),
financiallySupported: toYrn(result.financiallySupported),
updatedAai: toYrn(result.updatedAai)
};
} catch (e: unknown) {
throw e;
}
},

updateSubmission: async (formId: string, formSubmissionId: string, data: any) => {
updateSubmission: async (data: ChefsSubmissionForm) => {
try {
console.log('updateSubmission');
console.log(data);
await prisma.submission.update({
data: {
...data,
addedToATS: fromYrn(data.addedToATS),
financiallySupported: fromYrn(data.financiallySupported),
updatedAai: fromYrn(data.updatedAai)
},
where: {
submissionId: data.submissionId
}
});
} catch (e: unknown) {
throw e;
}
Expand Down
27 changes: 27 additions & 0 deletions app/src/types/ChefsSubmissionForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IStamps } from '../interfaces/IStamps';

export type ChefsSubmissionForm = {
submissionId: string;
assigneeToUserId?: string;
confirmationId: string;
contactEmail?: string;
contactPhoneNumber?: string;
contactFirstName?: string;
contactLastName?: string;
intakeStatus?: string;
projectName?: string;
queuePriority?: string;
singleFamilyUnits?: string;
streetAddress?: string;
atsClientNumber?: string;
addedToATS?: string;
financiallySupported?: string;
applicationStatus?: string;
relatedPermits?: string;
updatedAai?: string;
waitingOn?: string;
submittedAt: string;
submittedBy: string;
bringForwardDate?: string;
notes?: string;
} & IStamps;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type ChefsSubmissionDataSource = {
export type ChefsSubmissionFormExport = {
form: {
id: string;
submissionId: string;
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/form/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ type Props = {
label?: string;
name: string;
disabled?: boolean;
showTime?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
helpText: '',
type: 'text',
label: '',
disabled: false
disabled: false,
showTime: false
});
const { errorMessage, value } = useField<string>(toRef(props, 'name'));
Expand All @@ -31,7 +33,7 @@ const { errorMessage, value } = useField<string>(toRef(props, 'name'));
:name="name"
:class="'w-full ' + { 'p-invalid': errorMessage }"
:disabled="disabled"
show-time
:show-time="props.showTime"
hour-format="24"
show-icon
icon-display="input"
Expand Down
Loading

0 comments on commit c2dde06

Please sign in to comment.