Skip to content

Commit

Permalink
Merge branch 'main' into fix/add-missing-zendesk-config
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Nov 8, 2024
2 parents 1b50379 + b68eea7 commit 5929fc9
Show file tree
Hide file tree
Showing 54 changed files with 697 additions and 889 deletions.
2 changes: 2 additions & 0 deletions apps/application-system/api/infra/application-system-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ export const serviceSetup = (services: {
'/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_USERNAME',
ALTHINGI_OMBUDSMAN_XROAD_PASSWORD:
'/k8s/api/ALTHINGI_OMBUDSMAN_XROAD_PASSWORD',
NATIONAL_REGISTRY_B2C_CLIENT_SECRET:
'/k8s/api/NATIONAL_REGISTRY_B2C_CLIENT_SECRET',
})
.db()
.migrations()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class TemplateApiApplicationService extends BaseTemplateApiApplicationSer
super()
}

async saveAttachmentToApplicaton(
async saveAttachmentToApplication(
application: ApplicationWithAttachments,
fileName: string,
buffer: Buffer,
Expand Down
1 change: 0 additions & 1 deletion apps/auth-admin-web/specs/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react'
import { render } from '@testing-library/react'
import 'whatwg-fetch'
import Index from '../pages/index'
jest.mock('next/router', () => ({
useRouter: () => ({ push: () => jest.fn() }),
Expand Down
1 change: 1 addition & 0 deletions charts/islandis/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@ application-system-api:
IDENTITY_SERVER_CLIENT_SECRET: '/k8s/application-system/api/IDENTITY_SERVER_CLIENT_SECRET'
ISLYKILL_SERVICE_BASEPATH: '/k8s/api/ISLYKILL_SERVICE_BASEPATH'
ISLYKILL_SERVICE_PASSPHRASE: '/k8s/api/ISLYKILL_SERVICE_PASSPHRASE'
NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/api/NATIONAL_REGISTRY_B2C_CLIENT_SECRET'
NOVA_PASSWORD: '/k8s/application-system/api/NOVA_PASSWORD'
NOVA_URL: '/k8s/application-system-api/NOVA_URL'
SYSLUMENN_HOST: '/k8s/application-system-api/SYSLUMENN_HOST'
Expand Down
1 change: 1 addition & 0 deletions charts/islandis/values.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ application-system-api:
IDENTITY_SERVER_CLIENT_SECRET: '/k8s/application-system/api/IDENTITY_SERVER_CLIENT_SECRET'
ISLYKILL_SERVICE_BASEPATH: '/k8s/api/ISLYKILL_SERVICE_BASEPATH'
ISLYKILL_SERVICE_PASSPHRASE: '/k8s/api/ISLYKILL_SERVICE_PASSPHRASE'
NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/api/NATIONAL_REGISTRY_B2C_CLIENT_SECRET'
NOVA_PASSWORD: '/k8s/application-system/api/NOVA_PASSWORD'
NOVA_URL: '/k8s/application-system-api/NOVA_URL'
SYSLUMENN_HOST: '/k8s/application-system-api/SYSLUMENN_HOST'
Expand Down
1 change: 1 addition & 0 deletions charts/islandis/values.staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ application-system-api:
IDENTITY_SERVER_CLIENT_SECRET: '/k8s/application-system/api/IDENTITY_SERVER_CLIENT_SECRET'
ISLYKILL_SERVICE_BASEPATH: '/k8s/api/ISLYKILL_SERVICE_BASEPATH'
ISLYKILL_SERVICE_PASSPHRASE: '/k8s/api/ISLYKILL_SERVICE_PASSPHRASE'
NATIONAL_REGISTRY_B2C_CLIENT_SECRET: '/k8s/api/NATIONAL_REGISTRY_B2C_CLIENT_SECRET'
NOVA_PASSWORD: '/k8s/application-system/api/NOVA_PASSWORD'
NOVA_URL: '/k8s/application-system-api/NOVA_URL'
SYSLUMENN_HOST: '/k8s/application-system-api/SYSLUMENN_HOST'
Expand Down
4 changes: 2 additions & 2 deletions libs/api/domains/education/src/lib/education.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { FeatureFlagModule } from '@island.is/nest/feature-flags'
import { XRoadConfig, MMSApi } from '@island.is/clients/mms'
import { FriggResolver, InnaResolver, MainResolver } from './graphql'
import { EducationService } from './education.service'
import { S3Service } from './s3.service'
import { InnaClientModule } from '@island.is/clients/inna'
import { NationalRegistryV3ClientModule } from '@island.is/clients/national-registry-v3'
import { FriggClientModule } from '@island.is/clients/mms/frigg'
import { AwsModule } from '@island.is/nest/aws'

export interface Config {
fileDownloadBucket: string
Expand All @@ -23,7 +23,6 @@ export class EducationModule {
FriggResolver,
InnaResolver,
MainResolver,
S3Service,
EducationService,
{
provide: 'CONFIG',
Expand All @@ -39,6 +38,7 @@ export class EducationModule {
FeatureFlagModule,
NationalRegistryV3ClientModule,
FriggClientModule,
AwsModule,
],
exports: [],
}
Expand Down
29 changes: 23 additions & 6 deletions libs/api/domains/education/src/lib/education.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import {
ExamResult,
Student,
} from './education.type'
import { S3Service } from './s3.service'
import { getYearInterval } from './education.utils'
import { NationalRegistryV3ClientService } from '@island.is/clients/national-registry-v3'
import { isDefined } from '@island.is/shared/utils'
import { S3Service } from '@island.is/nest/aws'
import { LOGGER_PROVIDER, type Logger } from '@island.is/logging'

@Injectable()
export class EducationService {
Expand All @@ -30,6 +31,7 @@ export class EducationService {
@Inject('CONFIG')
private readonly config: Config,
private readonly nationalRegistryApi: NationalRegistryV3ClientService,
@Inject(LOGGER_PROVIDER) protected readonly logger: Logger,
) {}

async getLicenses(
Expand All @@ -53,11 +55,26 @@ export class EducationService {
nationalId,
licenseId,
)

return this.s3Service.uploadFileFromStream(responseStream, {
fileName: uuid(),
bucket: this.config.fileDownloadBucket,
})
try {
const fileLocation = await this.s3Service.uploadFile(
responseStream.body,
{ bucket: this.config.fileDownloadBucket, key: uuid() },
{
ContentType:
responseStream.headers?.get('content-type') || 'application/pdf',
},
)

// Presigned URL expires in 65 seconds to allow for download initiation
const PRESIGNED_URL_EXPIRY = 65
return await this.s3Service.getPresignedUrl(
fileLocation,
PRESIGNED_URL_EXPIRY,
)
} catch (error) {
this.logger.error(`Failed to process PDF license:`, { error, licenseId })
return null
}
}

async getFamily(nationalId: string): Promise<Array<Student>> {
Expand Down
4 changes: 2 additions & 2 deletions libs/api/domains/education/src/lib/education.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ADULT_STUDENT1,
} from './__mock-data__/my-family'
import { MMSApi } from '@island.is/clients/mms'
import { S3Service } from './s3.service'
import {
NationalRegistryV3ClientConfig,
NationalRegistryV3ClientModule,
Expand All @@ -24,6 +23,7 @@ import {
import { Student } from './education.type'
import { ConfigModule } from '@nestjs/config'
import { XRoadConfig } from '@island.is/nest/config'
import { AwsModule } from '@island.is/nest/aws'

const config = {
fileDownloadBucket: '',
Expand All @@ -39,13 +39,13 @@ describe('EducationService', () => {
imports: [
LoggingModule,
NationalRegistryV3ClientModule,
AwsModule,
ConfigModule.forRoot({
isGlobal: true,
load: [XRoadConfig, NationalRegistryV3ClientConfig],
}),
],
providers: [
S3Service,
{
provide: MMSApi,
useValue: new MMSApi(config.xroad),
Expand Down
69 changes: 0 additions & 69 deletions libs/api/domains/education/src/lib/s3.service.ts

This file was deleted.

4 changes: 2 additions & 2 deletions libs/api/domains/file-upload/src/lib/file-upload.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Args, Resolver, Mutation } from '@nestjs/graphql'
import { S3 } from 'aws-sdk'
import { PresignedPost } from './presignedPost.model'
import { FileStorageService } from '@island.is/file-storage'
import { PresignedPost as S3PresignedPost } from '@aws-sdk/s3-presigned-post'

@Resolver()
export class FileUploadResolver {
Expand All @@ -10,7 +10,7 @@ export class FileUploadResolver {
@Mutation(() => PresignedPost)
createUploadUrl(
@Args('filename') filename: string,
): Promise<S3.PresignedPost> {
): Promise<S3PresignedPost> {
return this.fileStorageService.generatePresignedPost(filename)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { getValueViaPath } from '@island.is/application/core'
import { ApplicationWithAttachments as Application } from '@island.is/application/types'
import { S3 } from 'aws-sdk'
import AmazonS3URI from 'amazon-s3-uri'
import { ApplicationWithAttachments } from '@island.is/application/types'
import { logger } from '@island.is/logging'
import { Injectable } from '@nestjs/common'
import { Inject, Injectable } from '@nestjs/common'
import { S3Service } from '@island.is/nest/aws'
import { ApplicationService } from '@island.is/application/api/core'
import { sharedModuleConfig } from '../shared.config'
import { ConfigType } from '@nestjs/config'
import { uuid } from 'uuidv4'

export interface AttachmentData {
key: string
Expand All @@ -14,13 +17,15 @@ export interface AttachmentData {

@Injectable()
export class AttachmentS3Service {
private readonly s3: AWS.S3
constructor() {
this.s3 = new S3()
}
constructor(
private readonly s3Service: S3Service,
@Inject(sharedModuleConfig.KEY)
private config: ConfigType<typeof sharedModuleConfig>,
private readonly applicationService: ApplicationService,
) {}

public async getFiles(
application: Application,
application: ApplicationWithAttachments,
attachmentAnswerKeys: string[],
): Promise<AttachmentData[]> {
const attachments: AttachmentData[] = []
Expand All @@ -43,7 +48,7 @@ export class AttachmentS3Service {
name: string
}>,
answerKey: string,
application: Application,
application: ApplicationWithAttachments,
): Promise<AttachmentData[]> {
return await Promise.all(
answers.map(async ({ key, name }) => {
Expand All @@ -58,30 +63,81 @@ export class AttachmentS3Service {
return { key: '', fileContent: '', answerKey, fileName: '' }
}
const fileContent =
(await this.getApplicationFilecontentAsBase64(url)) ?? ''
(await this.s3Service.getFileContent(url, 'base64')) ?? ''

return { key, fileContent, answerKey, fileName: name }
}),
)
}

private async getApplicationFilecontentAsBase64(
async addAttachment(
application: ApplicationWithAttachments,
fileName: string,
): Promise<string | undefined> {
const { bucket, key } = AmazonS3URI(fileName)
const uploadBucket = bucket
try {
const file = await this.s3
.getObject({
Bucket: uploadBucket,
Key: key,
})
.promise()
const fileContent = file.Body as Buffer
return fileContent?.toString('base64')
} catch (error) {
logger.error(error)
return undefined
buffer: Buffer,
uploadParameters?: {
ContentType?: string
ContentDisposition?: string
ContentEncoding?: string
},
): Promise<string> {
return this.saveAttachmentToApplication(
application,
fileName,
buffer,
uploadParameters,
)
}

async saveAttachmentToApplication(
application: ApplicationWithAttachments,
fileName: string,
buffer: Buffer,
uploadParameters?: {
ContentType?: string
ContentDisposition?: string
ContentEncoding?: string
},
): Promise<string> {
const uploadBucket = this.config.templateApi.attachmentBucket
if (!uploadBucket) throw new Error('No attachment bucket configured')

const fileId = uuid()
const attachmentKey = `${fileId}-${fileName}`
const s3key = `${application.id}/${attachmentKey}`
const url = await this.s3Service.uploadFile(
buffer,
{ bucket: uploadBucket, key: s3key },
uploadParameters,
)

await this.applicationService.update(application.id, {
attachments: {
...(application.attachments || {}),
[attachmentKey]: url,
},
})

return attachmentKey
}

async getAttachmentUrl(
application: ApplicationWithAttachments,
attachmentKey: string,
expiration: number,
): Promise<string> {
if (expiration <= 0) {
throw new Error('Expiration must be positive')
}
const fileName = (
application.attachments as {
[key: string]: string
}
)[attachmentKey]

if (!fileName) {
throw new Error('Attachment not found')
}

return this.s3Service.getPresignedUrl(fileName, expiration)
}
}
Loading

0 comments on commit 5929fc9

Please sign in to comment.