Skip to content

Commit fc7997a

Browse files
author
marcellmueller
committed
chore: test deployment
1 parent c3b9613 commit fc7997a

19 files changed

Lines changed: 6016 additions & 297 deletions

.github/workflows/main.yml

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,61 +20,62 @@ jobs:
2020
cancel-in-progress: true
2121
uses: ./.github/workflows/.build.yml
2222

23-
repo-analysis:
24-
name: Repository analysis
25-
concurrency:
26-
group: ${{ github.workflow }}-${{ github.ref }}-analysis
27-
cancel-in-progress: true
28-
uses: ./.github/workflows/.analysis.yml
29-
secrets: inherit
30-
31-
analysis-results:
32-
name: Analysis Results
33-
needs: [repo-analysis]
34-
if: always()
35-
runs-on: ubuntu-24.04
36-
steps:
37-
- if: contains(needs.*.result, 'failure')
38-
run: echo "At least one job has failed." && exit 1
39-
- run: echo "Success!"
40-
41-
test-admin:
42-
name: Run tests for admin app
43-
needs: build-containers
44-
uses: ./.github/workflows/.tests.yml
45-
with:
46-
app: admin
47-
secrets: inherit
48-
49-
test-public:
50-
name: Run tests for public app
51-
needs: build-containers
52-
uses: ./.github/workflows/.tests.yml
53-
with:
54-
app: public
55-
secrets: inherit
56-
57-
test-shared:
58-
name: Run shared tests
59-
needs: build-containers
60-
uses: ./.github/workflows/.shared.yml
61-
23+
# repo-analysis:
24+
# name: Repository analysis
25+
# concurrency:
26+
# group: ${{ github.workflow }}-${{ github.ref }}-analysis
27+
# cancel-in-progress: true
28+
# uses: ./.github/workflows/.analysis.yml
29+
# secrets: inherit
30+
#
31+
# analysis-results:
32+
# name: Analysis Results
33+
# needs: [repo-analysis]
34+
# if: always()
35+
# runs-on: ubuntu-24.04
36+
# steps:
37+
# - if: contains(needs.*.result, 'failure')
38+
# run: echo "At least one job has failed." && exit 1
39+
# - run: echo "Success!"
40+
#
41+
# test-admin:
42+
# name: Run tests for admin app
43+
# needs: build-containers
44+
# uses: ./.github/workflows/.tests.yml
45+
# with:
46+
# app: admin
47+
# secrets: inherit
48+
#
49+
# test-public:
50+
# name: Run tests for public app
51+
# needs: build-containers
52+
# uses: ./.github/workflows/.tests.yml
53+
# with:
54+
# app: public
55+
# secrets: inherit
56+
#
57+
# test-shared:
58+
# name: Run shared tests
59+
# needs: build-containers
60+
# uses: ./.github/workflows/.shared.yml
61+
#
6262
review-and-deploy-admin:
63-
if: github.ref_name == 'main' || github.head_ref == 'main'
63+
# if: github.ref_name == 'main' || github.head_ref == 'main'
6464
name: Review and deploy Admin app
65-
needs: [test-admin, test-public, test-shared]
65+
needs: build-containers
66+
# needs: [test-admin, test-public, test-shared]
6667
uses: ./.github/workflows/.review-and-deploy.yml
6768
with:
6869
app: admin
6970
tag: ${{ github.sha }}
7071
secrets: inherit
7172

72-
review-and-deploy-public:
73-
if: github.ref_name == 'main' || github.head_ref == 'main'
74-
name: Review and deploy Public app
75-
needs: [test-admin, test-public, test-shared]
76-
uses: ./.github/workflows/.review-and-deploy.yml
77-
with:
78-
app: public
79-
tag: ${{ github.sha }}
80-
secrets: inherit
73+
# review-and-deploy-public:
74+
# if: github.ref_name == 'main' || github.head_ref == 'main'
75+
# name: Review and deploy Public app
76+
# needs: [test-admin, test-public, test-shared]
77+
# uses: ./.github/workflows/.review-and-deploy.yml
78+
# with:
79+
# app: public
80+
# tag: ${{ github.sha }}
81+
# secrets: inherit

admin/backend/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ DAM_RST_IMAGE_COLLECTION_ID=728
1717
DAM_RESOURCE_TYPE_PDF=24
1818
DAM_RESOURCE_TYPE_IMAGE=23
1919
APP_ENV=local
20+
AWS_REGION=ca-central-1
21+
ESTABLISHMENT_ORDER_DOCS_BUCKET=rst-lza-establishment-order-docs-dev

admin/backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
},
2323
"dependencies": {
2424
"@aws-sdk/client-cloudwatch": "^3.876.0",
25+
"@aws-sdk/client-s3": "^3.911.0",
26+
"@aws-sdk/s3-request-presigner": "^3.911.0",
2527
"@nestjs/cli": "^11.0.10",
2628
"@nestjs/common": "^11.1.6",
2729
"@nestjs/config": "^4.0.2",

admin/backend/prisma/schema.prisma

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ model recreation_resource {
3636
recreation_district_code recreation_district_code? @relation(fields: [district_code], references: [district_code], onDelete: NoAction, onUpdate: NoAction)
3737
recreation_maintenance_standard_code recreation_maintenance_standard_code? @relation(fields: [maintenance_standard_code], references: [maintenance_standard_code], onDelete: NoAction, onUpdate: NoAction)
3838
recreation_resource_docs recreation_resource_docs[]
39+
recreation_establishment_order_docs recreation_establishment_order_docs[]
3940
recreation_resource_images recreation_resource_images[]
4041
recreation_resource_reservation_info recreation_resource_reservation_info?
4142
recreation_site_description recreation_site_description?
@@ -579,6 +580,24 @@ model recreation_resource_docs {
579580
@@schema("rst")
580581
}
581582

583+
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
584+
model recreation_establishment_order_docs {
585+
s3_key String @id @db.VarChar
586+
rec_resource_id String? @db.VarChar(10)
587+
title String? @db.VarChar
588+
file_size BigInt?
589+
extension String? @db.VarChar
590+
updated_at DateTime? @default(now()) @db.Timestamp(6)
591+
updated_by String?
592+
created_at DateTime? @default(now()) @db.Timestamp(6)
593+
created_by String?
594+
sys_period Unsupported("tstzrange") @default(dbgenerated("tstzrange(CURRENT_TIMESTAMP, NULL::timestamp with time zone)"))
595+
recreation_resource recreation_resource? @relation(fields: [rec_resource_id], references: [rec_resource_id], onDelete: NoAction, onUpdate: NoAction)
596+
597+
@@index([rec_resource_id], map: "idx_recreation_establishment_order_docs_rec_resource_id")
598+
@@schema("rst")
599+
}
600+
582601
model flyway_schema_history {
583602
installed_rank Int @id(map: "flyway_schema_history_pk")
584603
version String? @db.VarChar(50)

admin/backend/src/app-config/app-config.schema.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ export class EnvironmentVariables {
6767
@IsUrl({ require_tld: false })
6868
@IsNotEmpty()
6969
KEYCLOAK_ISSUER: string;
70+
71+
// AWS S3 configuration
72+
@IsString()
73+
@IsNotEmpty()
74+
ESTABLISHMENT_ORDER_DOCS_BUCKET: string;
75+
76+
@IsString()
77+
@IsNotEmpty()
78+
AWS_REGION: string;
7079
}
7180

7281
export function validate(config: Record<string, unknown>) {

admin/backend/src/app-config/app-config.service.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,17 @@ export class AppConfigService {
9292
get keycloakIssuer(): string {
9393
return this.configService.get('KEYCLOAK_ISSUER', { infer: true })!;
9494
}
95+
96+
// AWS S3 Configuration
97+
get establishmentOrderDocsBucket(): string {
98+
return this.configService.get('ESTABLISHMENT_ORDER_DOCS_BUCKET', {
99+
infer: true,
100+
})!;
101+
}
102+
103+
get awsRegion(): string {
104+
return this.configService.get('AWS_REGION', {
105+
infer: true,
106+
})!;
107+
}
95108
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
export class EstablishmentOrderDocDto {
4+
@ApiProperty({
5+
description: 'S3 object key for the document',
6+
example: 'REC0001/establishment-order.pdf',
7+
})
8+
s3_key: string;
9+
10+
@ApiProperty({
11+
description: 'Recreation Resource ID',
12+
example: 'REC0001',
13+
})
14+
rec_resource_id: string;
15+
16+
@ApiProperty({
17+
description: 'Title of the establishment order document',
18+
example: 'Establishment Order 2024',
19+
})
20+
title: string;
21+
22+
@ApiProperty({
23+
description: 'File size in bytes',
24+
example: 1024000,
25+
required: false,
26+
})
27+
file_size?: number;
28+
29+
@ApiProperty({
30+
description: 'File extension',
31+
example: 'pdf',
32+
required: false,
33+
})
34+
extension?: string;
35+
36+
@ApiProperty({
37+
description: 'Presigned URL for downloading the document',
38+
example: 'https://s3.amazonaws.com/...',
39+
})
40+
url: string;
41+
42+
@ApiProperty({
43+
description: 'When the document was created',
44+
example: '2024-01-01T00:00:00Z',
45+
required: false,
46+
})
47+
created_at?: Date;
48+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
2+
import {
3+
ApiBearerAuth,
4+
ApiOperation,
5+
ApiParam,
6+
ApiResponse,
7+
ApiTags,
8+
} from '@nestjs/swagger';
9+
import { EstablishmentOrderDocsService } from './establishment-order-docs.service';
10+
import { EstablishmentOrderDocDto } from './dto/establishment-order-doc.dto';
11+
import { AuthGuard } from '@nestjs/passport';
12+
import { AUTH_STRATEGY } from '@/auth';
13+
14+
@ApiTags('establishment-order-docs')
15+
@Controller({
16+
path: 'recreation-resources/:rec_resource_id/establishment-order-docs',
17+
version: '1',
18+
})
19+
@UseGuards(AuthGuard(AUTH_STRATEGY.KEYCLOAK))
20+
@ApiBearerAuth(AUTH_STRATEGY.KEYCLOAK)
21+
export class EstablishmentOrderDocsController {
22+
constructor(
23+
private readonly establishmentOrderDocsService: EstablishmentOrderDocsService,
24+
) {}
25+
26+
@Get()
27+
@ApiOperation({
28+
summary: 'Get all establishment order documents for a recreation resource',
29+
description:
30+
'Returns a list of establishment order documents with presigned URLs for download',
31+
})
32+
@ApiParam({
33+
name: 'rec_resource_id',
34+
description: 'Recreation Resource ID',
35+
example: 'REC0001',
36+
})
37+
@ApiResponse({
38+
status: 200,
39+
description: 'List of establishment order documents',
40+
type: [EstablishmentOrderDocDto],
41+
})
42+
@ApiResponse({
43+
status: 401,
44+
description: 'Unauthorized',
45+
})
46+
@ApiResponse({
47+
status: 404,
48+
description: 'Recreation resource not found',
49+
})
50+
async getAll(
51+
@Param('rec_resource_id') rec_resource_id: string,
52+
): Promise<EstablishmentOrderDocDto[]> {
53+
return this.establishmentOrderDocsService.getAll(rec_resource_id);
54+
}
55+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Module } from '@nestjs/common';
2+
import { PrismaModule } from '@/prisma.module';
3+
import { PrismaService } from '@/prisma.service';
4+
import { EstablishmentOrderDocsController } from './establishment-order-docs.controller';
5+
import { EstablishmentOrderDocsService } from './establishment-order-docs.service';
6+
import { S3Service } from './s3.service';
7+
8+
@Module({
9+
imports: [PrismaModule],
10+
controllers: [EstablishmentOrderDocsController],
11+
providers: [EstablishmentOrderDocsService, S3Service, PrismaService],
12+
exports: [EstablishmentOrderDocsService],
13+
})
14+
export class EstablishmentOrderDocsModule {}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Injectable, Logger } from '@nestjs/common';
2+
import { PrismaService } from '@/prisma.service';
3+
import { S3Service } from './s3.service';
4+
import { EstablishmentOrderDocDto } from './dto/establishment-order-doc.dto';
5+
6+
@Injectable()
7+
export class EstablishmentOrderDocsService {
8+
private readonly logger = new Logger(EstablishmentOrderDocsService.name);
9+
10+
constructor(
11+
private readonly prisma: PrismaService,
12+
private readonly s3Service: S3Service,
13+
) {}
14+
15+
/**
16+
* Get all establishment order documents for a recreation resource
17+
* Fetches metadata from database and generates presigned URLs
18+
* @param rec_resource_id - Recreation Resource ID
19+
* @returns Array of establishment order documents with presigned URLs
20+
*/
21+
async getAll(rec_resource_id: string): Promise<EstablishmentOrderDocDto[]> {
22+
this.logger.log(
23+
`Fetching establishment order docs for rec_resource_id: ${rec_resource_id}`,
24+
);
25+
26+
// Fetch all documents from database
27+
const docs = await this.prisma.recreation_establishment_order_docs.findMany(
28+
{
29+
where: {
30+
rec_resource_id,
31+
},
32+
select: {
33+
s3_key: true,
34+
rec_resource_id: true,
35+
title: true,
36+
file_size: true,
37+
extension: true,
38+
created_at: true,
39+
},
40+
},
41+
);
42+
43+
// Generate presigned URLs for each document
44+
const docsWithUrls = await Promise.all(
45+
docs.map(async (doc) => {
46+
const url = await this.s3Service.getSignedUrl(doc.s3_key);
47+
return {
48+
s3_key: doc.s3_key,
49+
rec_resource_id: doc.rec_resource_id!,
50+
title: doc.title || '',
51+
file_size: doc.file_size ? Number(doc.file_size) : 0,
52+
extension: doc.extension || '',
53+
url,
54+
created_at: doc.created_at || undefined,
55+
};
56+
}),
57+
);
58+
59+
return docsWithUrls;
60+
}
61+
}

0 commit comments

Comments
 (0)