Skip to content

Commit

Permalink
fix: Process formattedDate type response (#862)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsamojlenko authored Oct 7, 2024
1 parent d915a1e commit e56a600
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 1 deletion.
45 changes: 44 additions & 1 deletion lambda-code/reliability/lib/dataLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import {
TransactWriteCommand,
} from "@aws-sdk/lib-dynamodb";
import { v4 } from "uuid";
import { FormElement, FormSubmission, Responses, Response } from "./types.js";
import {
FormElement,
FormSubmission,
Responses,
Response,
DateFormat,
DateObject,
} from "./types.js";
import { getFormattedDateFromObject } from "./utils.js";

const awsProperties = {
region: process.env.REGION ?? "ca-central-1",
Expand Down Expand Up @@ -269,6 +277,14 @@ function handleType(
case "fileInput":
handleFileInputResponse(qTitle, response, collector);
break;
case "formattedDate":
handleFormattedDateResponse(
qTitle,
response,
question.properties.dateFormat as DateFormat,
collector
);
break;
default:
// Do not try to handle form elements like richText that do not have responses
break;
Expand Down Expand Up @@ -302,6 +318,14 @@ function handleDynamicForm(
case "checkbox":
handleArrayResponse(qTitle, (row as Record<string, Response>)[qIndex], rowCollector);
break;
case "formattedDate":
handleFormattedDateResponse(
qTitle,
(row as Record<string, Response>)[qIndex],
qItem.properties.dateFormat as DateFormat,
rowCollector
);
break;
default:
// Do not try to handle form elements like richText that do not have responses
break;
Expand Down Expand Up @@ -351,3 +375,22 @@ function handleFileInputResponse(title: string, response: Response, collector: s

collector.push(`**${title}**${String.fromCharCode(13)}No Response`);
}

function handleFormattedDateResponse(
title: string,
response: Response,
dateFormat: DateFormat,
collector: string[]
) {
if (response !== undefined && response !== null && response !== "") {
collector.push(
`**${title}**${String.fromCharCode(13)}${getFormattedDateFromObject(
dateFormat,
JSON.parse(String(response))
)}`
);
return;
}

collector.push(`**${title}**${String.fromCharCode(13)}No Response`);
}
17 changes: 17 additions & 0 deletions lambda-code/reliability/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type Response =
| Record<string, unknown>[]
| FileInputResponse
| FileInputResponse[]
| DateFormat
| Record<string, unknown>;

export type FileInputResponse = {
Expand Down Expand Up @@ -45,6 +46,7 @@ export interface ElementProperties {
isSectional?: boolean;
maxNumberOfRows?: number;
autoComplete?: string;
dateFormat?: string;
[key: string]:
| string
| number
Expand All @@ -70,6 +72,7 @@ export enum FormElementTypes {
name = "name",
firstMiddleLastName = "firstMiddleLastName",
contact = "contact",
formattedDate = "formattedDate",
}
// used to define attributes for a form element or field
export interface FormElement {
Expand Down Expand Up @@ -128,3 +131,17 @@ export interface DeliveryOption {
}

export type SecurityAttribute = "Unclassified" | "Protected A" | "Protected B";

export interface DateObject {
YYYY: number;
MM: number;
DD: number;
}

export enum DatePart {
DD = "day",
MM = "month",
YYYY = "year",
}

export type DateFormat = "YYYY-MM-DD" | "DD-MM-YYYY" | "MM-DD-YYYY";
114 changes: 114 additions & 0 deletions lambda-code/reliability/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { DateFormat, DateObject } from "./types.js";

/**
* Utility function to use when rendering a formatted date string
*
* @param dateFormat
* @param dateObject
* @returns string
*/
export const getFormattedDateFromObject = (
dateFormat: DateFormat = "YYYY-MM-DD",
dateObject: DateObject
): string => {
// If an invalid date format is provided, use the default format
if (!isValidDateFormat(dateFormat)) {
dateFormat = "YYYY-MM-DD";
}

// If the date object is invalid, return a dash
if (!isValidDateObject(dateObject)) {
return "-";
}

const { YYYY, MM, DD } = dateObject;

const formattedDate = dateFormat
.replace("YYYY", YYYY.toString())
.replace("MM", MM.toString().padStart(2, "0"))
.replace("DD", DD.toString().padStart(2, "0"));

return formattedDate;
};

/**
* Check that a DateObject is a valid date
*
* @param dateObject
* @returns boolean
*/
export const isValidDate = (dateObject: DateObject): boolean => {
if (!isValidDateObject(dateObject)) {
return false;
}

if (dateObject.YYYY < 1900) {
return false;
}

const { YYYY, MM, DD } = dateObject;
const date = new Date(YYYY, MM - 1, DD);

return date.getFullYear() === YYYY && date.getMonth() + 1 === MM && date.getDate() === DD;
};

/**
* Check that a date format is valid
*
* @param dateFormat
* @returns boolean
*/
export const isValidDateFormat = (dateFormat: DateFormat): boolean => {
return ["YYYY-MM-DD", "DD-MM-YYYY", "MM-DD-YYYY"].includes(dateFormat);
};

/**
* Check that a date object is valid
*
* @param obj
* @returns boolean
*/
export const isValidDateObject = (obj: unknown): obj is DateObject => {
return (
typeof obj === "object" &&
obj !== null &&
"YYYY" in obj &&
"MM" in obj &&
"DD" in obj &&
typeof obj.YYYY === "number" &&
typeof obj.MM === "number" &&
typeof obj.DD === "number"
);
};

/**
* Check if a year is a leap year
*
* @param year
* @returns boolean
*/
export const isLeapYear = (year: number) => {
return year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
};

/**
* Get the maximum number of days in a month
*
* @param month
* @param year
* @returns number
*/
export const getMaxMonthDay = (month: number, year: number) => {
// Months are 1-indexed
switch (month) {
case 2:
return isLeapYear(year) ? 29 : 28;
case 4:
case 6:
case 9:
case 11:
return 30;
default:
return 31;
}
};

0 comments on commit e56a600

Please sign in to comment.