From 9883d7801ef9566c5fb7424bf6dcae36b5ea2b49 Mon Sep 17 00:00:00 2001 From: praju-aot Date: Tue, 16 Jul 2024 16:30:59 -0400 Subject: [PATCH 1/3] feat: ORV2-2223 Policy Config Api - Phase 2 --- .../dbo.ORBC_FEATURE_FLAG.Table.sql | 10 ++ .../versions/revert/v_32_ddl_revert.sql | 31 ++++++ database/mssql/scripts/versions/v_32_ddl.sql | 58 +++++++++++ database/mssql/test/versions/v_32_1_test.sql | 4 + database/mssql/test/versions/v_32_2_test.sql | 5 + database/mssql/test/versions/v_32_3_test.sql | 5 + database/mssql/test/versions/v_32_test.sh | 30 ++++++ dops/src/enum/roles.enum.ts | 4 +- policy/src/app.module.ts | 2 + .../is-feature-flag-enabled.decorator.ts | 22 +++++ policy/src/enum/roles.enum.ts | 2 + .../policy-config.path-params.dto.ts | 16 ++++ .../get-policy-config.query-params.dto.ts | 19 ++++ .../dto/response/read-policy-config.dto.ts | 53 +++++++++++ .../entities/policy-config.entity.ts | 72 ++++++++++++++ .../policy-config/policy-config.controller.ts | 95 +++++++++++++++++++ .../policy-config/policy-config.module.ts | 13 +++ .../policy-config/policy-config.service.ts | 90 ++++++++++++++++++ .../profile/policy-config.profile.ts | 26 +++++ vehicles/src/common/enum/roles.enum.ts | 4 +- 20 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 database/mssql/scripts/versions/revert/v_32_ddl_revert.sql create mode 100644 database/mssql/scripts/versions/v_32_ddl.sql create mode 100644 database/mssql/test/versions/v_32_1_test.sql create mode 100644 database/mssql/test/versions/v_32_2_test.sql create mode 100644 database/mssql/test/versions/v_32_3_test.sql create mode 100644 database/mssql/test/versions/v_32_test.sh create mode 100644 policy/src/decorator/is-feature-flag-enabled.decorator.ts create mode 100644 policy/src/modules/policy-config/dto/request/pathParam/policy-config.path-params.dto.ts create mode 100644 policy/src/modules/policy-config/dto/request/queryParam/get-policy-config.query-params.dto.ts create mode 100644 policy/src/modules/policy-config/dto/response/read-policy-config.dto.ts create mode 100644 policy/src/modules/policy-config/entities/policy-config.entity.ts create mode 100644 policy/src/modules/policy-config/policy-config.controller.ts create mode 100644 policy/src/modules/policy-config/policy-config.module.ts create mode 100644 policy/src/modules/policy-config/policy-config.service.ts create mode 100644 policy/src/modules/policy-config/profile/policy-config.profile.ts diff --git a/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql b/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql index 1d8fb0755..56629d812 100644 --- a/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql +++ b/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql @@ -22,5 +22,15 @@ SET IDENTITY_INSERT [dbo].[ORBC_FEATURE_FLAG] ON ,[DB_LAST_UPDATE_TIMESTAMP]) VALUES ('2','APPLICATION-SEARCH','ENABLED', NULL, N'dbo', GETUTCDATE(), N'dbo', GETUTCDATE()); + INSERT INTO [dbo].[ORBC_FEATURE_FLAG] ([FEATURE_ID] + ,[FEATURE_KEY] + ,[FEATURE_VALUE] + ,[CONCURRENCY_CONTROL_NUMBER] + ,[DB_CREATE_USERID] + ,[DB_CREATE_TIMESTAMP] + ,[DB_LAST_UPDATE_USERID] + ,[DB_LAST_UPDATE_TIMESTAMP]) + VALUES ('3','POLICY-CONFIG','ENABLED', NULL, N'dbo', GETUTCDATE(), N'dbo', GETUTCDATE()); + SET IDENTITY_INSERT [dbo].[ORBC_FEATURE_FLAG] OFF GO diff --git a/database/mssql/scripts/versions/revert/v_32_ddl_revert.sql b/database/mssql/scripts/versions/revert/v_32_ddl_revert.sql new file mode 100644 index 000000000..91e8caf84 --- /dev/null +++ b/database/mssql/scripts/versions/revert/v_32_ddl_revert.sql @@ -0,0 +1,31 @@ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +SET NOCOUNT ON +GO + +SET XACT_ABORT ON + +BEGIN TRY + BEGIN TRANSACTION + + DROP CONSTRAINT [DF_ORBC_POLICY_CONFIGURATION_IS_DRAFT] + ALTER TABLE [dbo].[ORBC_POLICY_CONFIGURATION] DROP COLUMN [APP_LAST_UPDATE_USERID]; + + DELETE FROM [access].[ORBC_GROUP_ROLE] WHERE ROLE_TYPE IN ('ORBC-WRITE-POLICY-CONFIG','ORBC-READ-POLICY-CONFIG') + DELETE FROM [access].[ORBC_ROLE_TYPE] WHERE ROLE_TYPE IN ('ORBC-WRITE-POLICY-CONFIG','ORBC-READ-POLICY-CONFIG') + + COMMIT +END TRY + +BEGIN CATCH + IF @@TRANCOUNT > 0 + ROLLBACK; + THROW +END CATCH + +DECLARE @VersionDescription VARCHAR(255) +SET @VersionDescription = 'Reverting addition of APP_CREATE_USERID and APP_LAST_UPDATE_USERID to ORBC_POLICY_CONFIGURATION.' + +INSERT [dbo].[ORBC_SYS_VERSION] ([VERSION_ID], [DESCRIPTION], [RELEASE_DATE]) VALUES (31, @VersionDescription, getutcdate()) diff --git a/database/mssql/scripts/versions/v_32_ddl.sql b/database/mssql/scripts/versions/v_32_ddl.sql new file mode 100644 index 000000000..67e3fdbe3 --- /dev/null +++ b/database/mssql/scripts/versions/v_32_ddl.sql @@ -0,0 +1,58 @@ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +SET NOCOUNT ON +GO + +SET XACT_ABORT ON +GO + +SET TRANSACTION ISOLATION LEVEL SERIALIZABLE +GO + +BEGIN TRANSACTION +GO + +IF @@ERROR <> 0 + SET NOEXEC ON +GO + +INSERT [access].[ORBC_ROLE_TYPE] ([ROLE_TYPE], [ROLE_DESCRIPTION]) VALUES (N'ORBC-WRITE-POLICY-CONFIG', NULL) +INSERT [access].[ORBC_ROLE_TYPE] ([ROLE_TYPE], [ROLE_DESCRIPTION]) VALUES (N'ORBC-READ-POLICY-CONFIG', NULL) +GO + +INSERT [access].[ORBC_GROUP_ROLE] ([USER_AUTH_GROUP_TYPE], [ROLE_TYPE]) VALUES (N'SYSADMIN', N'ORBC-WRITE-POLICY-CONFIG') +INSERT [access].[ORBC_GROUP_ROLE] ([USER_AUTH_GROUP_TYPE], [ROLE_TYPE]) VALUES (N'SYSADMIN', N'ORBC-READ-POLICY-CONFIG') + +ALTER TABLE [dbo].[ORBC_POLICY_CONFIGURATION] ADD [APP_LAST_UPDATE_USERID] [nvarchar](30) NULL +GO + +ALTER TABLE [dbo].[ORBC_POLICY_CONFIGURATION] ADD CONSTRAINT [DF_ORBC_POLICY_CONFIGURATION_IS_DRAFT] DEFAULT ('Y') FOR [IS_DRAFT] +GO + +IF @@ERROR <> 0 SET NOEXEC ON +GO + +DECLARE @VersionDescription VARCHAR(255) +SET @VersionDescription = 'Add APP_LAST_UPDATE_USERID cols to ORBC_POLICY_CONFIGURATION' + +INSERT [dbo].[ORBC_SYS_VERSION] ([VERSION_ID], [DESCRIPTION], [UPDATE_SCRIPT], [REVERT_SCRIPT], [RELEASE_DATE]) VALUES (32, @VersionDescription, '$(UPDATE_SCRIPT)', '$(REVERT_SCRIPT)', getutcdate()) +IF @@ERROR <> 0 SET NOEXEC ON +GO + +COMMIT TRANSACTION +GO +IF @@ERROR <> 0 SET NOEXEC ON +GO +DECLARE @Success AS BIT +SET @Success = 1 +SET NOEXEC OFF +IF (@Success = 1) PRINT 'The database update succeeded' +ELSE BEGIN + IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION + PRINT 'The database update failed' +END +GO \ No newline at end of file diff --git a/database/mssql/test/versions/v_32_1_test.sql b/database/mssql/test/versions/v_32_1_test.sql new file mode 100644 index 000000000..ab25cf79e --- /dev/null +++ b/database/mssql/test/versions/v_32_1_test.sql @@ -0,0 +1,4 @@ +-- Test that the APP_LAST_UPDATE_USERID column has been added correctly +SET NOCOUNT ON + +select COL_LENGTH('$(DB_NAME).[dbo].[ORBC_POLICY_CONFIGURATION]', 'APP_LAST_UPDATE_USERID') diff --git a/database/mssql/test/versions/v_32_2_test.sql b/database/mssql/test/versions/v_32_2_test.sql new file mode 100644 index 000000000..11d576e28 --- /dev/null +++ b/database/mssql/test/versions/v_32_2_test.sql @@ -0,0 +1,5 @@ +-- Test that the role types have been inserted correctly +SET NOCOUNT ON + +SELECT COUNT(*) FROM $(DB_NAME).[access].[ORBC_ROLE_TYPE] +WHERE ROLE_TYPE IN ('ORBC-WRITE-POLICY-CONFIG','ORBC-READ-POLICY-CONFIG') \ No newline at end of file diff --git a/database/mssql/test/versions/v_32_3_test.sql b/database/mssql/test/versions/v_32_3_test.sql new file mode 100644 index 000000000..0ff430b4a --- /dev/null +++ b/database/mssql/test/versions/v_32_3_test.sql @@ -0,0 +1,5 @@ +-- Test that the role types have been inserted correctly against user auth groups +SET NOCOUNT ON + +SELECT COUNT(*) FROM $(DB_NAME).[access].[ORBC_GROUP_ROLE] +WHERE ROLE_TYPE IN ('ORBC-WRITE-POLICY-CONFIG','ORBC-READ-POLICY-CONFIG') \ No newline at end of file diff --git a/database/mssql/test/versions/v_32_test.sh b/database/mssql/test/versions/v_32_test.sh new file mode 100644 index 000000000..941c2fa96 --- /dev/null +++ b/database/mssql/test/versions/v_32_test.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Retrieve arguments +source ${SCRIPT_DIR}/utility/getopt.sh +USAGE="-u USER -p PASS -s SERVER -d DATABASE" +parse_options "${USAGE}" ${@} + +# All database tests for database version 32 are run from this shell script. +# TESTS_DIR variable set by the calling test-runner script. + +TEST_32_1_RESULT=$(/opt/mssql-tools/bin/sqlcmd -U ${USER} -P "${PASS}" -S ${SERVER} -v DB_NAME=${DATABASE} -h -1 -i ${TESTS_DIR}/v_32_1_test.sql | xargs) +if [[ $TEST_32_1_RESULT -eq 30 ]]; then + echo "Test 32.1 passed: APP_LAST_UPDATE_USERID column created in ORBC_POLICY_CONFIGURATION" +else + echo "******** Test 32.1 failed: APP_LAST_UPDATE_USERID column missing in ORBC_POLICY_CONFIGURATION" +fi + +TEST_32_2_RESULT=$(/opt/mssql-tools/bin/sqlcmd -U ${USER} -P "${PASS}" -S ${SERVER} -v DB_NAME=${DATABASE} -h -1 -i ${TESTS_DIR}/v_32_2_test.sql | xargs) +if [[ $TEST_32_2_RESULT -eq 2 ]]; then + echo "Test 32.2 passed: Role types inserted correctly" +else + echo "******** Test 32.2 failed: Role types not inserted correctly" +fi + +TEST_32_3_RESULT=$(/opt/mssql-tools/bin/sqlcmd -U ${USER} -P "${PASS}" -S ${SERVER} -v DB_NAME=${DATABASE} -h -1 -i ${TESTS_DIR}/v_32_3_test.sql | xargs) +if [[ $TEST_32_3_RESULT -eq 2 ]]; then + echo "Test 32.3 passed: Correct number of role mappings inserted" +else + echo "******** Test 32.3 failed: Incorrect number of role mappings inserted" +fi \ No newline at end of file diff --git a/dops/src/enum/roles.enum.ts b/dops/src/enum/roles.enum.ts index cfd711e9c..0b036762e 100644 --- a/dops/src/enum/roles.enum.ts +++ b/dops/src/enum/roles.enum.ts @@ -45,5 +45,7 @@ export enum Role { WRITE_LCV_FLAG = 'ORBC-WRITE-LCV-FLAG', READ_LOA = 'ORBC-READ-LOA', WRITE_LOA = 'ORBC-WRITE-LOA', - SEND_NOTIFICATION = 'ORBC-SEND-NOTIFICATION', + SEND_NOTIFICATION = 'ORBC-SEND-NOTIFICATION', + READ_POLICY_CONFIG = 'ORBC-READ-POLICY-CONFIG', + WRITE_POLICY_CONFIG = 'ORBC-WRITE-POLICY-CONFIG', } diff --git a/policy/src/app.module.ts b/policy/src/app.module.ts index 6ceef1472..529d405dc 100644 --- a/policy/src/app.module.ts +++ b/policy/src/app.module.ts @@ -23,6 +23,7 @@ import { getTypeormLogLevel } from './helper/logger.helper'; import { ClsModule } from 'nestjs-cls'; import { Request } from 'express'; import { v4 as uuidv4 } from 'uuid'; +import { PolicyConfigModule } from './modules/policy-config/policy-config.module'; const envPath = path.resolve(process.cwd() + '/../'); @@ -72,6 +73,7 @@ const envPath = path.resolve(process.cwd() + '/../'); CommonModule, AuthModule, FeatureFlagsModule, + PolicyConfigModule, ], controllers: [AppController], providers: [AppService], diff --git a/policy/src/decorator/is-feature-flag-enabled.decorator.ts b/policy/src/decorator/is-feature-flag-enabled.decorator.ts new file mode 100644 index 000000000..124383e01 --- /dev/null +++ b/policy/src/decorator/is-feature-flag-enabled.decorator.ts @@ -0,0 +1,22 @@ +import { Reflector } from '@nestjs/core'; + +/** + * Decorator to check if a specific feature flag is enabled. + * + * The feature flag can be used to conditionally enable or disable parts of the application + * depending on the current configuration or environment setup. + * + * Usage: + * + * ```typescript + * @IsFeatureFlagEnabled('featureName') + * async someFunction() { + * // function implementation + * } + * ``` + * + * @param {string} flagName - The name of the feature flag to check. + * @returns {MethodDecorator} The method decorator to be applied. + */ + +export const IsFeatureFlagEnabled = Reflector.createDecorator(); diff --git a/policy/src/enum/roles.enum.ts b/policy/src/enum/roles.enum.ts index cfd711e9c..facecc8e8 100644 --- a/policy/src/enum/roles.enum.ts +++ b/policy/src/enum/roles.enum.ts @@ -46,4 +46,6 @@ export enum Role { READ_LOA = 'ORBC-READ-LOA', WRITE_LOA = 'ORBC-WRITE-LOA', SEND_NOTIFICATION = 'ORBC-SEND-NOTIFICATION', + READ_POLICY_CONFIG = 'ORBC-READ-POLICY-CONFIG', + WRITE_POLICY_CONFIG = 'ORBC-WRITE-POLICY-CONFIG', } diff --git a/policy/src/modules/policy-config/dto/request/pathParam/policy-config.path-params.dto.ts b/policy/src/modules/policy-config/dto/request/pathParam/policy-config.path-params.dto.ts new file mode 100644 index 000000000..7b23c2b8e --- /dev/null +++ b/policy/src/modules/policy-config/dto/request/pathParam/policy-config.path-params.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsPositive } from 'class-validator'; +import { Type } from 'class-transformer'; + +export class PolicyConfigIdPathParamDto { + @ApiProperty({ + example: 1, + description: + 'The unique identifier of the policy configuration. This field is required.', + required: true, + }) + @IsInt() + @IsPositive() + @Type(() => Number) + policyConfigId: number; +} diff --git a/policy/src/modules/policy-config/dto/request/queryParam/get-policy-config.query-params.dto.ts b/policy/src/modules/policy-config/dto/request/queryParam/get-policy-config.query-params.dto.ts new file mode 100644 index 000000000..a06e15938 --- /dev/null +++ b/policy/src/modules/policy-config/dto/request/queryParam/get-policy-config.query-params.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; +import { Transform } from 'class-transformer'; + +export class GetPolicyConfigQueryParamsDto { + @ApiProperty({ + description: + 'A flag indicating to fetch only the current policy configuration.', + example: true, + default: false, + required: false, + }) + @IsOptional() + @Transform(({ obj, key }: { obj: Record; key: string }) => { + return obj[key] === 'true' ? true : obj[key] === 'false' ? false : obj[key]; + }) + @IsBoolean() + isCurrent?: boolean = false; +} diff --git a/policy/src/modules/policy-config/dto/response/read-policy-config.dto.ts b/policy/src/modules/policy-config/dto/response/read-policy-config.dto.ts new file mode 100644 index 000000000..2f43db29c --- /dev/null +++ b/policy/src/modules/policy-config/dto/response/read-policy-config.dto.ts @@ -0,0 +1,53 @@ +import { AutoMap } from '@automapper/classes'; +import { ApiProperty } from '@nestjs/swagger'; + +export class ReadPolicyConfigDto { + /** + * Unique identifier for the policy configuration. + */ + @AutoMap() + @ApiProperty({ + example: '1', + description: 'Unique identifier for the policy configuration.', + }) + policyConfigId: number; + + /** + * JSON data representing the policy configuration. + */ + @AutoMap() + @ApiProperty({ + description: 'Policy configuration in JSON format.', + }) + policy: JSON; + + /** + * Configuration effective date. + */ + @AutoMap() + @ApiProperty({ + example: '2023-07-13T17:31:17.470Z', + description: 'Policy Configuration effective date.', + }) + effectiveDate: string; + + /** + * Indicates if the configuration is currently a draft version. + */ + @AutoMap() + @ApiProperty({ + example: true, + description: 'Indicates if the configuration is currently a draft.', + }) + isDraft: boolean; + + /** + * Description of changes made in the configuration. + */ + @AutoMap() + @ApiProperty({ + example: 'Initial release of policy configuration with updated rules', + description: 'Description of changes made in the configuration.', + }) + changeDescription: string; +} diff --git a/policy/src/modules/policy-config/entities/policy-config.entity.ts b/policy/src/modules/policy-config/entities/policy-config.entity.ts new file mode 100644 index 000000000..183215d95 --- /dev/null +++ b/policy/src/modules/policy-config/entities/policy-config.entity.ts @@ -0,0 +1,72 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { Base } from '../../common/entities/base.entity'; +import { AutoMap } from '@automapper/classes'; +import { ApiProperty } from '@nestjs/swagger'; + +@Entity({ name: 'ORBC_POLICY_CONFIGURATION', schema: 'dbo' }) +export class PolicyConfig extends Base { + /** + * Unique identifier for the policy configuration. + */ + @AutoMap() + @ApiProperty({ + example: '1', + description: 'Unique identifier for the policy configuration.', + }) + @PrimaryGeneratedColumn({ type: 'int', name: 'POLICY_CONFIGURATION_ID' }) + policyConfigId: number; + + /** + * JSON data representing the policy configuration. + */ + @AutoMap() + @Column({ name: 'POLICY_JSON', nullable: true, type: 'simple-json' }) //Full text search capabilities is not required on the field + policy: JSON; + + /** + * configuration effective date. + */ + @AutoMap() + @ApiProperty({ + example: '2023-07-13T17:31:17.470Z', + description: 'Configuration effective date.', + }) + @Column({ + name: 'EFFECTIVE_DATE', + nullable: true, + type: 'datetime2', + }) + effectiveDate: Date; + + /** + * Specifies whether the config is currently active. 'Y' for yes, 'N' for no. + */ + @AutoMap() + @Column({ + type: 'char', + name: 'IS_DRAFT', + default: true, + nullable: false, + transformer: { + to: (value: boolean): string => (value ? 'Y' : 'N'), // Converts the boolean value to 'Y' or 'N' for storage. + from: (value: string): boolean => value === 'Y', // Converts the stored string back to a boolean. + }, + }) + isDraft: boolean; + + /** + * Description of changes made in the configuration. + */ + @AutoMap() + @ApiProperty({ + example: 'Initial release of policy configuration with updated rules', + description: 'Description of changes made in the configuration.', + }) + @Column({ + name: 'CHANGE_DESCRIPTION', + nullable: true, + type: 'nvarchar', + length: 2000, + }) + changeDescription: string; +} diff --git a/policy/src/modules/policy-config/policy-config.controller.ts b/policy/src/modules/policy-config/policy-config.controller.ts new file mode 100644 index 000000000..bcb0388b1 --- /dev/null +++ b/policy/src/modules/policy-config/policy-config.controller.ts @@ -0,0 +1,95 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { PolicyConfigService } from './policy-config.service'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiMethodNotAllowedResponse, + ApiOkResponse, + ApiOperation, + ApiTags, + ApiUnprocessableEntityResponse, +} from '@nestjs/swagger'; +import { ExceptionDto } from '../../exception/exception.dto'; +import { IsFeatureFlagEnabled } from '../../decorator/is-feature-flag-enabled.decorator'; +import { GetPolicyConfigQueryParamsDto } from './dto/request/queryParam/get-policy-config.query-params.dto'; +import { Roles } from '../../decorator/roles.decorator'; +import { PolicyConfigIdPathParamDto } from './dto/request/pathParam/policy-config.path-params.dto'; +import { Role } from '../../enum/roles.enum'; +import { ReadPolicyConfigDto } from './dto/response/read-policy-config.dto'; +import { AuthOnly } from '../../decorator/auth-only.decorator'; + +@ApiBearerAuth() +@ApiTags('Policy Configuration') +@ApiMethodNotAllowedResponse({ + description: 'The Policy Api Method Not Allowed Response', + type: ExceptionDto, +}) +@ApiInternalServerErrorResponse({ + description: 'The Policy Api Internal Server Error Response', + type: ExceptionDto, +}) +@ApiUnprocessableEntityResponse({ + description: 'The Policy Entity could not be processed.', + type: ExceptionDto, +}) +@ApiBadRequestResponse({ + description: 'Bad Request Response', + type: ExceptionDto, +}) +@IsFeatureFlagEnabled('POLICY-CONFIG') +@Controller('policy-config') +export class PolicyConfigController { + constructor(private readonly policyConfigService: PolicyConfigService) {} + + /** + * Retrieves active policy configurations. + * + * @param {GetPolicyConfigQueryParamsDto} query - The query parameters. + * @returns A promise that resolves to an array of active policy configurations. + */ + @ApiOperation({ + summary: 'Retrieves active policy configurations.', + description: 'Retrieves active policy configurations.', + }) + @ApiOkResponse({ + description: 'The retrieved active policy configurations.', + type: ReadPolicyConfigDto, + }) + @AuthOnly() + @Get() + async findAllActive( + @Query() + { isCurrent }: GetPolicyConfigQueryParamsDto, + ): Promise { + return await this.policyConfigService.findAllActive(isCurrent); + } + + /** + * Retrieves draft policy configurations. + * + * @returns A promise that resolves to an array of draft policy configurations. + */ + @ApiOperation({ + summary: 'Retrieves draft policy configurations.', + description: + 'Retrieves draft policy configurations, enforcing authentication.', + }) + @ApiOkResponse({ + description: 'The retrieved draft policy configurations.', + type: ReadPolicyConfigDto, + }) + @Roles(Role.READ_POLICY_CONFIG) + @Get('/draft') + async findAllDraft(): Promise { + return await this.policyConfigService.findAllDraft(); + } + + @Roles(Role.READ_POLICY_CONFIG) + @Get(':policyConfigId') + async findOne( + @Param() { policyConfigId }: PolicyConfigIdPathParamDto, + ): Promise { + return await this.policyConfigService.findOne(policyConfigId); + } +} diff --git a/policy/src/modules/policy-config/policy-config.module.ts b/policy/src/modules/policy-config/policy-config.module.ts new file mode 100644 index 000000000..5ecc8567a --- /dev/null +++ b/policy/src/modules/policy-config/policy-config.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { PolicyConfigService } from './policy-config.service'; +import { PolicyConfigController } from './policy-config.controller'; +import { PolicyConfig } from './entities/policy-config.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { PolicyConfigProfile } from './profile/policy-config.profile'; + +@Module({ + imports: [TypeOrmModule.forFeature([PolicyConfig])], + controllers: [PolicyConfigController], + providers: [PolicyConfigService, PolicyConfigProfile], +}) +export class PolicyConfigModule {} diff --git a/policy/src/modules/policy-config/policy-config.service.ts b/policy/src/modules/policy-config/policy-config.service.ts new file mode 100644 index 000000000..e186301a0 --- /dev/null +++ b/policy/src/modules/policy-config/policy-config.service.ts @@ -0,0 +1,90 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { PolicyConfig } from './entities/policy-config.entity'; +import { Mapper } from '@automapper/core'; +import { Repository } from 'typeorm'; +import { InjectMapper } from '@automapper/nestjs'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Nullable } from '../../types/common'; +import { ReadPolicyConfigDto } from './dto/response/read-policy-config.dto'; + +@Injectable() +export class PolicyConfigService { + private readonly logger = new Logger(PolicyConfigService.name); + constructor( + @InjectMapper() private readonly classMapper: Mapper, + @InjectRepository(PolicyConfig) + private readonly policyConfigRepository: Repository, + ) {} + + /** + * Retrieves all active policy configurations based on the provided conditions. + * + * @param {Nullable} isCurrent - If true, only current active policies are returned based on the effective date. + * @returns {Promise} - A promise that resolves to an array of ReadPolicyConfigDto objects. + */ + async findAllActive( + isCurrent?: Nullable, + ): Promise { + const policyConfigQB = + this.policyConfigRepository.createQueryBuilder('policy'); + policyConfigQB.where('policy.isDraft = :isDraft', { isDraft: 'N' }); + if (isCurrent) { + const currentUTCDate = new Date().toISOString(); + policyConfigQB + .andWhere('policy.effectiveDate <= :effectiveDate', { + effectiveDate: currentUTCDate, + }) + .orderBy('policy.effectiveDate', 'DESC') + .addOrderBy('policy.policyConfigId', 'DESC') + .limit(1); + } else { + policyConfigQB + .orderBy('policy.effectiveDate', 'DESC') + .addOrderBy('policy.policyConfigId', 'DESC'); + } + return this.classMapper.mapArrayAsync( + await policyConfigQB.getMany(), + PolicyConfig, + ReadPolicyConfigDto, + ); + } + + /** + * Retrieves all draft policy configurations. + * + * @returns {Promise} - A promise that resolves to an array of ReadPolicyConfigDto objects. + */ + async findAllDraft(): Promise { + const policyConfigQB = + this.policyConfigRepository.createQueryBuilder('policy'); + policyConfigQB.where('policy.isDraft = :isDraft', { isDraft: 'Y' }); + policyConfigQB + .orderBy('policy.effectiveDate', 'DESC') + .addOrderBy('policy.policyConfigId', 'DESC'); + + return this.classMapper.mapArrayAsync( + await policyConfigQB.getMany(), + PolicyConfig, + ReadPolicyConfigDto, + ); + } + + /** + * Retrieves a policy configuration based on the provided policyConfigId. + * + * @param {number} policyConfigId - The ID of the policy configuration to retrieve. + * @returns {Promise} - A promise that resolves to a ReadPolicyConfigDto object. + */ + async findOne(policyConfigId: number): Promise { + const policyConfigQB = + this.policyConfigRepository.createQueryBuilder('policy'); + policyConfigQB.where('policy.policyConfigId = :policyConfigId', { + policyConfigId, + }); + return this.classMapper.mapAsync( + await policyConfigQB.getOne(), + PolicyConfig, + ReadPolicyConfigDto, + ); + } +} diff --git a/policy/src/modules/policy-config/profile/policy-config.profile.ts b/policy/src/modules/policy-config/profile/policy-config.profile.ts new file mode 100644 index 000000000..856ecdf55 --- /dev/null +++ b/policy/src/modules/policy-config/profile/policy-config.profile.ts @@ -0,0 +1,26 @@ +import { AutomapperProfile, InjectMapper } from '@automapper/nestjs'; +import { Mapper, createMap, forMember, mapFrom } from '@automapper/core'; +import { Injectable } from '@nestjs/common'; +import { PolicyConfig } from '../entities/policy-config.entity'; +import { ReadPolicyConfigDto } from '../dto/response/read-policy-config.dto'; + +@Injectable() +export class PolicyConfigProfile extends AutomapperProfile { + constructor(@InjectMapper() mapper: Mapper) { + super(mapper); + } + + override get profile() { + return (mapper: Mapper) => { + createMap( + mapper, + PolicyConfig, + ReadPolicyConfigDto, + forMember( + (d) => d.policy, + mapFrom((s) => s.policy), + ), + ); + }; + } +} diff --git a/vehicles/src/common/enum/roles.enum.ts b/vehicles/src/common/enum/roles.enum.ts index cebea5846..5c30e718b 100644 --- a/vehicles/src/common/enum/roles.enum.ts +++ b/vehicles/src/common/enum/roles.enum.ts @@ -49,5 +49,7 @@ export enum Role { WRITE_SUSPEND = 'ORBC-WRITE-SUSPEND', SEND_NOTIFICATION = 'ORBC-SEND-NOTIFICATION', WRITE_CREDIT_ACCOUNT = 'ORBC-WRITE-CREDIT-ACCOUNT', - READ_CREDIT_ACCOUNT = 'ORBC-READ-CREDIT-ACCOUNT', + READ_CREDIT_ACCOUNT = 'ORBC-READ-CREDIT-ACCOUNT', + READ_POLICY_CONFIG = 'ORBC-READ-POLICY-CONFIG', + WRITE_POLICY_CONFIG = 'ORBC-WRITE-POLICY-CONFIG', } From 59a5da8ab6ee08287ceb76705df6d0836087ff4f Mon Sep 17 00:00:00 2001 From: praju-aot Date: Tue, 16 Jul 2024 16:36:25 -0400 Subject: [PATCH 2/3] Swagger documentation --- .../policy-config/policy-config.controller.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/policy/src/modules/policy-config/policy-config.controller.ts b/policy/src/modules/policy-config/policy-config.controller.ts index bcb0388b1..47ac87f36 100644 --- a/policy/src/modules/policy-config/policy-config.controller.ts +++ b/policy/src/modules/policy-config/policy-config.controller.ts @@ -55,6 +55,7 @@ export class PolicyConfigController { @ApiOkResponse({ description: 'The retrieved active policy configurations.', type: ReadPolicyConfigDto, + isArray: true, }) @AuthOnly() @Get() @@ -78,6 +79,7 @@ export class PolicyConfigController { @ApiOkResponse({ description: 'The retrieved draft policy configurations.', type: ReadPolicyConfigDto, + isArray: true, }) @Roles(Role.READ_POLICY_CONFIG) @Get('/draft') @@ -85,6 +87,22 @@ export class PolicyConfigController { return await this.policyConfigService.findAllDraft(); } + /** + * Retrieves a specific policy configuration inlcuding draft policy. + * + * @param {PolicyConfigIdPathParamDto} params - The path parameters containing the policyConfigId. + * @returns A promise that resolves to a policy configuration. + */ + @ApiOperation({ + summary: + 'Retrieves a specific policy configuration inlcuding draft policy.', + description: + 'Retrieves a specific policy configuration by its ID, enforcing authentication.', + }) + @ApiOkResponse({ + description: 'The retrieved draft policy configuration.', + type: ReadPolicyConfigDto, + }) @Roles(Role.READ_POLICY_CONFIG) @Get(':policyConfigId') async findOne( From 219d0f075e34f119038b1052bc465ac74a1570d4 Mon Sep 17 00:00:00 2001 From: praju-aot Date: Tue, 16 Jul 2024 16:40:57 -0400 Subject: [PATCH 3/3] Code review comment --- policy/src/modules/policy-config/policy-config.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policy/src/modules/policy-config/policy-config.controller.ts b/policy/src/modules/policy-config/policy-config.controller.ts index 47ac87f36..a7fa1777c 100644 --- a/policy/src/modules/policy-config/policy-config.controller.ts +++ b/policy/src/modules/policy-config/policy-config.controller.ts @@ -38,7 +38,7 @@ import { AuthOnly } from '../../decorator/auth-only.decorator'; type: ExceptionDto, }) @IsFeatureFlagEnabled('POLICY-CONFIG') -@Controller('policy-config') +@Controller('policy-configurations') export class PolicyConfigController { constructor(private readonly policyConfigService: PolicyConfigService) {}