Skip to content
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

Add ability to manually kick-off subrecipient file generation #497

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions api/src/functions/processValidationJson/processValidationJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,21 @@ export const processRecord = async (

try {
const subrecipientKey = `treasuryreports/${organizationId}/${reportingPeriod.id}/subrecipients.json`
const { startDate, endDate } = reportingPeriod
const startDate = new Date(
reportingPeriod.endDate.getFullYear(),
reportingPeriod.endDate.getMonth() + 1,
1
)
const endDate = new Date(
reportingPeriod.endDate.getFullYear(),
reportingPeriod.endDate.getMonth() + 2,
0
)
const subrecipientsWithUploads = await db.subrecipient.findMany({
where: { createdAt: { lte: endDate, gte: startDate } },
where: {
createdAt: { lte: endDate, gte: startDate },
organizationId,
},
include: { subrecipientUploads: true },
})
const subrecipients = {
Expand Down
114 changes: 94 additions & 20 deletions api/src/services/subrecipients/subrecipients.scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,65 @@ export const standard = defineScenario<
| Prisma.SubrecipientCreateArgs
| Prisma.SubrecipientUploadCreateArgs
>({
reportingPeriod: {
one: () => ({
data: {
name: 'String',
startDate: '2024-01-26T15:11:27.688Z',
endDate: '2024-01-26T15:11:27.688Z',
inputTemplate: {
create: {
name: 'String',
version: 'String',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
outputTemplate: {
create: {
name: 'String',
version: 'String',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
},
}),
q3: () => ({
data: {
name: 'Q3 2024 [July 1 - September 30]',
startDate: '2024-07-01T00:00:00.000Z',
endDate: '2024-09-30T00:00:00.000Z',
inputTemplate: {
create: {
name: 'Q3 - input',
version: '2024_q3',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
outputTemplate: {
create: {
name: 'Q3 - output',
version: '2024_q3',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
},
}),
},
organization: {
one: {
data: {
name: 'USDR',
preferences: {},
},
},
two: (scenario) => ({
data: {
name: 'Q3 Testing Org',
preferences: {
current_reporting_period_id: scenario.reportingPeriod.q3.id,
},
},
}),
},
agency: {
one: (scenario) => ({
Expand All @@ -37,6 +89,16 @@ export const standard = defineScenario<
organization: true,
},
}),
two: (scenario) => ({
data: {
name: 'Q3Agency',
organizationId: scenario.organization.two.id,
code: 'AQ3',
},
include: {
organization: true,
},
}),
},
user: {
one: (scenario) => ({
Expand All @@ -50,27 +112,15 @@ export const standard = defineScenario<
agency: true,
},
}),
},
reportingPeriod: {
one: () => ({
two: (scenario) => ({
data: {
name: 'String',
startDate: '2024-01-26T15:11:27.688Z',
endDate: '2024-01-26T15:11:27.688Z',
inputTemplate: {
create: {
name: 'String',
version: 'String',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
outputTemplate: {
create: {
name: 'String',
version: 'String',
effectiveDate: '2024-01-26T15:11:27.688Z',
},
},
email: '[email protected]',
name: 'Q3 User',
role: 'USDR_ADMIN',
agencyId: scenario.agency.two.id,
},
include: {
agency: true,
},
}),
},
Expand All @@ -89,6 +139,30 @@ export const standard = defineScenario<
ueiTinCombo: '12485_920485',
},
}),
q3_createdOctober: (scenario) => ({
data: {
name: 'October Subrecipient',
organization: { connect: { id: scenario.organization.two.id } },
ueiTinCombo: '17290_172900',
createdAt: '2024-10-15T00:00:00.000Z',
},
}),
q3_createdNovember: (scenario) => ({
data: {
name: 'November Subrecipient',
organization: { connect: { id: scenario.organization.two.id } },
ueiTinCombo: '17291_172911',
createdAt: '2024-11-26T00:00:00.000Z',
},
}),
q3_createdSeptember: (scenario) => ({
data: {
name: 'September Subrecipient',
organization: { connect: { id: scenario.organization.two.id } },
ueiTinCombo: '17292_172922',
createdAt: '2024-09-26T00:00:00.000Z',
},
}),
},
subrecipientUpload: {
one: (scenario) => ({
Expand Down
27 changes: 25 additions & 2 deletions api/src/services/subrecipients/subrecipients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import type { GraphQLResolveInfo } from 'graphql'

import type { RedwoodGraphQLContext } from '@redwoodjs/graphql-server'

import { sendPutObjectToS3Bucket } from 'src/lib/aws'

import {
subrecipients,
subrecipient,
createSubrecipient,
updateSubrecipient,
deleteSubrecipient,
Subrecipient as SubrecipientResolver,
uploadSubrecipients,
} from './subrecipients'
import type { StandardScenario } from './subrecipients.scenarios'

Expand All @@ -18,13 +21,16 @@ import type { StandardScenario } from './subrecipients.scenarios'
// Please refer to the RedwoodJS Testing Docs:
// https://redwoodjs.com/docs/testing#testing-services
// https://redwoodjs.com/docs/testing#jest-expect-type-considerations

jest.mock('src/lib/aws', () => ({
...jest.requireActual('src/lib/aws'),
sendPutObjectToS3Bucket: jest.fn(),
}))
describe('subrecipients', () => {
scenario('returns all subrecipients', async (scenario: StandardScenario) => {
mockCurrentUser(scenario.user.one)
const result = await subrecipients()

expect(result.length).toEqual(Object.keys(scenario.subrecipient).length)
expect(result.length).toEqual(2)
})

scenario(
Expand Down Expand Up @@ -122,4 +128,21 @@ describe('subrecipients', () => {
)
}
)

scenario(
'uploads all valid newly created subrecipients',
async (scenario: StandardScenario) => {
// uploadSubrecipients
const result = await uploadSubrecipients({
input: {
organizationId: scenario.organization.two.id,
reportingPeriodId: scenario.reportingPeriod.q3.id,
},
})
expect(sendPutObjectToS3Bucket).toHaveBeenCalled()
expect(result.message).toEqual('Subrecipients uploaded successfully')
expect(result.success).toBe(true)
expect(result.countSubrecipients).toBe(1)
}
)
})
60 changes: 60 additions & 0 deletions api/src/services/subrecipients/subrecipients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import type {
SubrecipientRelationResolvers,
} from 'types/graphql'

import { sendPutObjectToS3Bucket } from 'src/lib/aws'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'

export const subrecipients: QueryResolvers['subrecipients'] = () => {
const currentUser = context.currentUser
Expand Down Expand Up @@ -48,6 +50,64 @@ export const deleteSubrecipient: MutationResolvers['deleteSubrecipient'] = ({
})
}

export const uploadSubrecipients: MutationResolvers['uploadSubrecipients'] =
async ({ input }) => {
const { organizationId, reportingPeriodId } = input
const organization = await db.organization.findUnique({
where: { id: organizationId },
})
const reportingPeriod = await db.reportingPeriod.findFirst({
where: { id: reportingPeriodId },
})
if (!organization || !reportingPeriod) {
throw new Error('Organization or reporting period not found')
}
if (
organization.preferences?.current_reporting_period_id !==
reportingPeriod.id
) {
throw new Error(
'Reporting period does not match current reporting period'
)
}

try {
const subrecipientKey = `treasuryreports/${organizationId}/${reportingPeriodId}/subrecipients.json`

const startDate = new Date(
reportingPeriod.endDate.getFullYear(),
reportingPeriod.endDate.getMonth() + 1,
1
)
const endDate = new Date(
reportingPeriod.endDate.getFullYear(),
reportingPeriod.endDate.getMonth() + 2,
0
)

const subrecipientsWithUploads = await db.subrecipient.findMany({
where: { createdAt: { lte: endDate, gte: startDate }, organizationId },
include: { subrecipientUploads: true },
})
const subrecipients = {
subrecipients: subrecipientsWithUploads,
}
await sendPutObjectToS3Bucket(
`${process.env.REPORTING_DATA_BUCKET_NAME}`,
subrecipientKey,
JSON.stringify(subrecipients)
)
return {
message: 'Subrecipients uploaded successfully',
success: true,
countSubrecipients: subrecipientsWithUploads.length,
}
} catch (err) {
logger.error(`Error saving subrecipients JSON file to S3: ${err}`)
throw new Error('Error saving subrecipient info to S3')
}
}

export const Subrecipient: SubrecipientRelationResolvers = {
organization: (_obj, { root }) => {
return db.subrecipient
Expand Down
11 changes: 11 additions & 0 deletions terraform/functions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,17 @@ module "lambda_function-graphql" {
"${module.reporting_data_bucket.bucket_arn}/treasuryreports/output-templates/*/*.xlsx",
]
}
AllowUploadSubrecipientsFile = {
effect = "Allow"
actions = [
"s3:PutObject",
]
resources = [
# These are temporary files shared across services containing subrecipient data.
# Path: treasuryreports/{organization_id}/{reporting_period_id}/subrecipients.json
"${module.reporting_data_bucket.bucket_arn}/treasuryreports/*/*/subrecipients.json",
]
}

AllowStepFunctionInvocation = {
effect = "Allow"
Expand Down
Loading