-
Notifications
You must be signed in to change notification settings - Fork 43
Feat: S3 private bucket pre-signed URL generation integration #6494
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
d282965
a2c6e37
ab92cf5
d0236a1
12574e3
341f8a6
c525147
2dfaa1f
72db937
e837c20
10c017d
47dc422
a9d3466
89ee266
8f9aee5
b076667
9049351
5946255
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,18 @@ | ||
| import { ApiProperty } from '@nestjs/swagger'; | ||
| import { Expose } from 'class-transformer'; | ||
| import { IsDefined, IsString } from 'class-validator'; | ||
| import { ValidationsGroupsEnum } from '../../enums/shared/validation-groups-enum'; | ||
|
|
||
| export class ApplicationBulkPresignedUrl { | ||
| @Expose() | ||
| @IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
| @IsDefined({ groups: [ValidationsGroupsEnum.default] }) | ||
| @ApiProperty() | ||
| key: string; | ||
|
|
||
| @Expose() | ||
| @IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
| @IsDefined({ groups: [ValidationsGroupsEnum.default] }) | ||
| @ApiProperty() | ||
| presignedUrl: string; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { ApiProperty } from '@nestjs/swagger'; | ||
| import { Expose } from 'class-transformer'; | ||
| import { IsDefined, MinLength, IsString, IsUUID } from 'class-validator'; | ||
| import { ValidationsGroupsEnum } from '../../enums/shared/validation-groups-enum'; | ||
|
|
||
| export class ApplicationBulkUrl { | ||
| @Expose() | ||
| @IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
| @MinLength(1, { groups: [ValidationsGroupsEnum.default] }) | ||
| @IsUUID(4, { groups: [ValidationsGroupsEnum.default] }) | ||
| @IsDefined({ groups: [ValidationsGroupsEnum.applicants] }) | ||
| @ApiProperty() | ||
| listingId: string; | ||
|
|
||
| @Expose() | ||
| @IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
| @MinLength(1, { groups: [ValidationsGroupsEnum.default] }) | ||
| @IsUUID(4, { groups: [ValidationsGroupsEnum.default] }) | ||
| @IsDefined({ groups: [ValidationsGroupsEnum.applicants] }) | ||
| @ApiProperty() | ||
| userId: string; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,12 @@ import { ListingService } from './listing.service'; | |
| import { PermissionService } from './permission.service'; | ||
| import { permissionActions } from '../enums/permissions/permission-actions-enum'; | ||
| import { convertApplicationDeclineReasonToReadable } from '../utilities/application-export-helpers'; | ||
| import { ApplicationBulkUrl } from '../dtos/applications/application-bulk-url.dto'; | ||
| import { doJurisdictionHaveFeatureFlagSet } from '../utilities/feature-flag-utilities'; | ||
| import { FeatureFlagEnum } from '../enums/feature-flags/feature-flags-enum'; | ||
| import { Jurisdiction } from '../dtos/jurisdictions/jurisdiction.dto'; | ||
| import { ApplicationBulkPresignedUrl } from '../dtos/applications/application-bulk-presigned-url.dto'; | ||
| import { S3Service } from './s3.service'; | ||
|
|
||
| const NUMBER_TO_PAGINATE_BY = 500; | ||
|
|
||
|
|
@@ -30,6 +36,7 @@ export class ApplicationBulkUploadService { | |
| private prisma: PrismaService, | ||
| private listingService: ListingService, | ||
| private permissionService: PermissionService, | ||
| private s3Service: S3Service, | ||
| ) {} | ||
|
|
||
| private formatApplicationStatus(statusEnum: ApplicationStatusEnum): string { | ||
|
|
@@ -295,6 +302,71 @@ export class ApplicationBulkUploadService { | |
| return stringData; | ||
| } | ||
|
|
||
| // TODO Yazeed - Integrate the uploadUrl method with S3 presigned URL generation logic | ||
| async uploadUrl( | ||
| dto: ApplicationBulkUrl, | ||
| ): Promise<ApplicationBulkPresignedUrl> { | ||
| const { userId, listingId } = dto; | ||
|
|
||
| const listingData = await this.prisma.listings.findUnique({ | ||
| select: { | ||
| // id: true, | ||
| jurisdictionId: true, | ||
|
Collaborator
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. we should specify the listing id is responded here so we aren't returning the full listing object
Collaborator
Author
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. We are already limiting the listing response object to only have the |
||
| jurisdictions: { | ||
| select: { | ||
| featureFlags: true, | ||
| }, | ||
| }, | ||
| }, | ||
| where: { | ||
| id: listingId, | ||
| }, | ||
| }); | ||
|
|
||
| if (!listingData) { | ||
| throw new NotFoundException( | ||
| `Listing with id: ${listingId} can not be found`, | ||
| ); | ||
| } | ||
|
|
||
| const requestingUser = await this.prisma.userAccounts.findFirst({ | ||
| where: { | ||
| id: userId, | ||
| }, | ||
| }); | ||
|
|
||
| await this.permissionService.canOrThrow( | ||
| mapTo(User, requestingUser), | ||
| 'listing', | ||
| permissionActions.update, | ||
| { | ||
| id: listingId, | ||
| jurisdictionId: listingData.jurisdictionId, | ||
| }, | ||
| ); | ||
|
|
||
| if ( | ||
| !doJurisdictionHaveFeatureFlagSet( | ||
| mapTo(Jurisdiction, listingData.jurisdictions), | ||
| FeatureFlagEnum.enableApplicationStatus, | ||
| ) | ||
| ) { | ||
| throw new ForbiddenException( | ||
| `Jurisdiction with id: ${listingData.jurisdictionId} does not have the enableApplicationStatus flag enabled`, | ||
| ); | ||
| } | ||
|
|
||
| const s3KeyTemplate = `bulk-application-updates-${listingId}-${userId}-${new Date().toISOString()}`; | ||
| const presignedUrl = await this.s3Service.uploadURLForPrivate( | ||
| s3KeyTemplate, | ||
| ); | ||
|
|
||
| return { | ||
| key: s3KeyTemplate, | ||
| presignedUrl: presignedUrl, | ||
| }; | ||
| } | ||
|
|
||
| async authorizeExport(user, listingId): Promise<void> { | ||
| /** | ||
| * Checking authorization for each application is very expensive. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.