Skip to content

Commit

Permalink
Merge pull request #407 from Enterprise-CMCS/master
Browse files Browse the repository at this point in the history
Release to val
  • Loading branch information
mdial89f authored Feb 23, 2024
2 parents 9d23c48 + 2661a99 commit 1b7f4a0
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 55 deletions.
8 changes: 4 additions & 4 deletions src/packages/shared-types/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export const attachmentTitleMap: Record<string, string> = {
currentStatePlan: "Current State Plan",
spaPages: "SPA Pages",
coverLetter: "Cover Letter",
tribalEngagement: "Tribal Engagement",
existingStatePlanPages: "Existing State Plan Pages",
tribalEngagement: "Document Demonstrating Good-Faith Tribal Engagement",
existingStatePlanPages: "Existing State Plan Page(s)",
publicNotice: "Public Notice",
sfq: "SFQ",
sfq: "Standard Funding Questions (SFQs)",
tribalConsultation: "Tribal Consultation",
amendedLanguage: "Amended Language",
amendedLanguage: "Amended State Plan Language",
budgetDocuments: "Budget Documents",
// ISSUE RAI
formalRaiLetter: "Formal RAI Letter",
Expand Down
13 changes: 7 additions & 6 deletions src/packages/shared-types/opensearch/main/transforms/seatool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import {
SeatoolOfficer,
} from "../../..";

import {
Authority,
SEATOOL_AUTHORITIES,
} from "shared-types";
import { Authority, SEATOOL_AUTHORITIES } from "shared-types";

type Flavor = "SPA" | "WAIVER" | "MEDICAID" | "CHIP";

Expand Down Expand Up @@ -150,7 +147,7 @@ export const transform = (id: string) => {
const authorityId = data.PLAN_TYPES?.[0].PLAN_TYPE_ID;
const typeId = data.STATE_PLAN_SERVICETYPES?.[0]?.SERVICE_TYPE_ID;
const subTypeId = data.STATE_PLAN_SERVICE_SUBTYPES?.[0]?.SERVICE_SUBTYPE_ID;
return {
const resp = {
id,
flavor: flavorLookup(data.STATE_PLAN.PLAN_TYPE), // This is MEDICAID CHIP or WAIVER... our concept
actionType: data.ACTIONTYPES?.[0].ACTION_NAME,
Expand Down Expand Up @@ -189,7 +186,11 @@ export const transform = (id: string) => {
seatoolStatus,
flavorLookup(data.STATE_PLAN.PLAN_TYPE)
),
raiWithdrawEnabled: !finalDispositionStatuses.includes(cmsStatus)
? undefined
: true,
};
return resp;
});
};
export type Schema = ReturnType<typeof transform>;
Expand Down Expand Up @@ -220,4 +221,4 @@ export const tombstone = (id: string) => {
submissionDate: null,
subject: null,
};
};
};
60 changes: 34 additions & 26 deletions src/packages/shared-utils/seatool-date-helper.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
import moment from "moment-timezone";
import * as fedHolidays from '@18f/us-federal-holidays';
import * as fedHolidays from "@18f/us-federal-holidays";

// Takes a local epoch for a moment in time, and returns the UTC epoch for that same moment
export const offsetToUtc = (date: Date): Date => {
return new Date(date.getTime() - date.getTimezoneOffset() * 60000);
};

// This manually accounts for the offset between the client's timezone and UTC.
export const offsetForUtc = (date: Date): Date => {
return new Date(date.getTime() - (date.getTimezoneOffset() * 60000));
}
// Takes a UTC epoch for a moment in time, and returns the local epoch for that same moment
export const offsetFromUtc = (date: Date): Date => {
return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
};

// This creates a Date for midnight today, then accounts for timezone offset.
export const seaToolFriendlyTimestamp = (date?: Date): number => {
// If you don't pass a date, we assume you want today the timestamp for today, midnight, utc.
if(!date) {
if (!date) {
date = new Date();
date.setHours(0,0,0,0);
date.setHours(0, 0, 0, 0);
}
return offsetForUtc(date).getTime();
return offsetToUtc(date).getTime();
};

// This takes an epoch string and converts it to a standard format for display
export const formatSeatoolDate = (date: string): string => {
return moment(date).tz("UTC").format("MM/DD/yyyy")
}
return moment(date).tz("UTC").format("MM/DD/yyyy");
};

export const getNextBusinessDayTimestamp = (date: Date = new Date()): number => {
let localeStringDate = date.toLocaleString("en-US", { timeZone: "America/New_York", dateStyle: "short" });
let localeStringHours24 = date.toLocaleString("en-US", { timeZone: "America/New_York", hour: 'numeric', hour12: false });
export const getNextBusinessDayTimestamp = (
date: Date = new Date()
): number => {
let localeStringDate = date.toLocaleString("en-US", {
timeZone: "America/New_York",
dateStyle: "short",
});
let localeStringHours24 = date.toLocaleString("en-US", {
timeZone: "America/New_York",
hour: "numeric",
hour12: false,
});
let localeDate = new Date(localeStringDate);

console.log(`Evaluating ${localeStringDate} at ${localeStringHours24}`);

const after5pmEST = parseInt(localeStringHours24,10) >= 17
const isHoliday = fedHolidays.isAHoliday(localeDate)
const isWeekend = !(localeDate.getDay() % 6)
if(after5pmEST || isHoliday || isWeekend) {
const after5pmEST = parseInt(localeStringHours24, 10) >= 17;
const isHoliday = fedHolidays.isAHoliday(localeDate);
const isWeekend = !(localeDate.getDay() % 6);
if (after5pmEST || isHoliday || isWeekend) {
let nextDate = localeDate;
nextDate.setDate(nextDate.getDate() + 1);
nextDate.setHours(12,0,0,0)
console.log("Current date is not valid. Will try " + nextDate)
return getNextBusinessDayTimestamp(nextDate)
nextDate.setHours(12, 0, 0, 0);
return getNextBusinessDayTimestamp(nextDate);
}

// Return the next business day's epoch for midnight UTC
let ret = offsetForUtc(localeDate).getTime();
console.log('Current date is a valid business date. Will return ' + ret);
let ret = offsetToUtc(localeDate).getTime();
return ret;
}
};
41 changes: 41 additions & 0 deletions src/services/api/handlers/itemExists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { response } from "../libs/handler";
import { APIGatewayEvent } from "aws-lambda";
import * as os from "../../../libs/opensearch-lib";

export const handler = async (event: APIGatewayEvent) => {
if (!event.body) {
return response({
statusCode: 400,
body: { message: "Event body required" },
});
}
try {
const body = JSON.parse(event.body);
const packageResult = await os.search(process.env.osDomain!, "main", {
query: {
match_phrase: {
id: {
query: body.id,
},
},
},
});
if (packageResult?.hits.total.value == 0) {
return response({
statusCode: 200,
body: { message: "No record found for the given id", exists: false },
});
} else {
return response({
statusCode: 200,
body: { message: "Record found for the given id", exists: true },
});
}
} catch (error) {
console.error({ error });
return response({
statusCode: 500,
body: { message: "Internal server error" },
});
}
};
18 changes: 18 additions & 0 deletions src/services/api/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,24 @@ functions:
subnetIds: >-
${self:custom.vpc.privateSubnets}
provisionedConcurrency: ${param:itemProvisionedConcurrency}
itemExists:
handler: handlers/itemExists.handler
maximumRetryAttempts: 0
environment:
region: ${self:provider.region}
osDomain: ${param:osDomain}
events:
- http:
path: /itemExists
method: post
cors: true
authorizer: aws_iam
vpc:
securityGroupIds:
- Ref: SecurityGroup
subnetIds: >-
${self:custom.vpc.privateSubnets}
provisionedConcurrency: ${param:itemProvisionedConcurrency}
submit:
handler: handlers/submit.handler
maximumRetryAttempts: 0
Expand Down
7 changes: 6 additions & 1 deletion src/services/data/handlers/sinkMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,12 @@ const onemac = async (kafkaRecords: KafkaRecord[], topicPartition: string) => {
.safeParse(record);
}
})();

if (result === undefined) {
console.log(
`no action to take for ${id} action ${record.actionType}. Continuing...`
);
continue;
}
if (!result?.success) {
logError({
type: ErrorType.VALIDATION,
Expand Down
1 change: 1 addition & 0 deletions src/services/ui/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./useGetForm";
export * from "./useGetItem";
export * from "./getAttachmentUrl";
export * from "./useGetPackageActions";
export * from "./itemExists";
6 changes: 6 additions & 0 deletions src/services/ui/src/api/itemExists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { API } from "aws-amplify";

export const itemExists = async (id: string): Promise<boolean> => {
const response = await API.post("os", "/itemExists", { body: { id } });
return response.exists;
};
9 changes: 0 additions & 9 deletions src/services/ui/src/api/useGetItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ export const getItem = async (
): Promise<opensearch.main.ItemResult> =>
await API.post("os", "/item", { body: { id } });

export const idIsUnique = async (id: string) => {
try {
await getItem(id);
return false;
} catch (e) {
return true;
}
};

export const idIsApproved = async (id: string) => {
try {
const record = await getItem(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { cn } from "@/lib/utils";
import { Button, Calendar, Input } from "@/components/Inputs";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/Popover";
import { opensearch } from "shared-types";
import { getNextBusinessDayTimestamp, offsetFromUtc } from "shared-utils";

type Props = Omit<
React.HTMLAttributes<HTMLDivElement>,
Expand Down Expand Up @@ -174,7 +175,7 @@ export function FilterableDateRange({ value, onChange, ...props }: Props) {
sideOffset={1}
>
<Calendar
disabled={[{ after: new Date() }]}
disabled={[{ after: offsetFromUtc(new Date(getNextBusinessDayTimestamp())) }]}
initialFocus
mode="range"
defaultMonth={selectedDate?.from}
Expand Down
15 changes: 7 additions & 8 deletions src/services/ui/src/pages/form/zod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";
import { isAuthorizedState } from "@/utils";
import { idIsApproved, idIsUnique } from "@/api";
import { idIsApproved, itemExists } from "@/api";

export const zSpaIdSchema = z
.string()
Expand All @@ -12,7 +12,7 @@ export const zSpaIdSchema = z
message:
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
.refine(async (value) => idIsUnique(value), {
.refine(async (value) => !(await itemExists(value)), {
message:
"According to our records, this SPA ID already exists. Please check the SPA ID and try entering it again.",
});
Expand Down Expand Up @@ -48,8 +48,7 @@ export const zInitialWaiverNumberSchema = z
message:
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
// TODO: update idIsUnique with proper check
.refine(async (value) => idIsUnique(value), {
.refine(async (value) => !(await itemExists(value)), {
message:
"According to our records, this 1915(b) Waiver Number already exists. Please check the 1915(b) Waiver Number and try entering it again.",
});
Expand All @@ -64,7 +63,7 @@ export const zRenewalWaiverNumberSchema = z
message:
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
.refine(async (value) => idIsUnique(value), {
.refine(async (value) => !(await itemExists(value)), {
message:
"According to our records, this 1915(b) Waiver Renewal Number already exists. Please check the 1915(b) Waiver Renewal Number and try entering it again.",
});
Expand All @@ -79,7 +78,7 @@ export const zAmendmentWaiverNumberSchema = z
message:
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
.refine(async (value) => idIsUnique(value), {
.refine(async (value) => !(await itemExists(value)), {
message:
"According to our records, this 1915(b) Waiver Amendment Number already exists. Please check the 1915(b) Waiver Amendment Number and try entering it again.",
});
Expand All @@ -95,7 +94,7 @@ export const zAmendmentOriginalWaiverNumberSchema = z
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
// This should already exist.
.refine(async (value) => !(await idIsUnique(value)), {
.refine(async (value) => await itemExists(value), {
message:
"According to our records, this 1915(b) Waiver Number does not yet exist. Please check the 1915(b) Waiver Amendment Number and try entering it again.",
})
Expand All @@ -114,7 +113,7 @@ export const zRenewalOriginalWaiverNumberSchema = z
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
// This should already exist
.refine(async (value) => !(await idIsUnique(value)), {
.refine(async (value) => await itemExists(value), {
message:
"According to our records, this 1915(b) Waiver Number does not yet exist. Please check the 1915(b) Waiver Amendment Number and try entering it again.",
})
Expand Down

0 comments on commit 1b7f4a0

Please sign in to comment.