From d1d68429ab6bab037ecb6d02831d0d8e4b27753d Mon Sep 17 00:00:00 2001 From: Krishnan Subramanian <84348052+krishnan-aot@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:54:00 -0800 Subject: [PATCH] ORV2-3069 Special Authorization Feature flag split (#1677) --- .../dbo.ORBC_FEATURE_FLAG.Table.sql | 46 ++++++ .../common/authentication/PermissionMatrix.ts | 8 +- .../dashboard/ManageSettingsDashboard.tsx | 20 ++- .../settings/hooks/specialAuthorizations.ts | 3 +- .../SpecialAuthorizations.tsx | 134 ++++++++++-------- .../special-auth/special-auth.controller.ts | 3 +- 6 files changed, 136 insertions(+), 78 deletions(-) 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 df88332e4..f37300398 100644 --- a/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql +++ b/database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql @@ -211,6 +211,52 @@ VALUES 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 + ( + '10', + 'LCV', + '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 + ( + '11', + 'NO-FEE', + 'ENABLED', + NULL, + N'dbo', + GETUTCDATE(), + N'dbo', + GETUTCDATE() + ); + SET IDENTITY_INSERT [dbo].[ORBC_FEATURE_FLAG] OFF GO \ No newline at end of file diff --git a/frontend/src/common/authentication/PermissionMatrix.ts b/frontend/src/common/authentication/PermissionMatrix.ts index 093895385..55f29b652 100644 --- a/frontend/src/common/authentication/PermissionMatrix.ts +++ b/frontend/src/common/authentication/PermissionMatrix.ts @@ -237,10 +237,10 @@ const MANAGE_SETTINGS = { * Special Authorizations Tab */ VIEW_SPECIAL_AUTHORIZATIONS: { allowedIDIRRoles: ALL_IDIR_ROLES }, - ADD_NO_FEE_FLAG: { allowedIDIRRoles: [SA, FIN, HQA] }, - UPDATE_NO_FEE_FLAG: { allowedIDIRRoles: [SA, FIN, HQA] }, - ADD_LCV_FLAG: { allowedIDIRRoles: [HQA] }, - REMOVE_LCV_FLAG: { allowedIDIRRoles: [HQA] }, + ADD_NO_FEE_FLAG: { allowedIDIRRoles: [SA, HQA] }, + UPDATE_NO_FEE_FLAG: { allowedIDIRRoles: [SA, HQA] }, + ADD_LCV_FLAG: { allowedIDIRRoles: [SA, HQA] }, + REMOVE_LCV_FLAG: { allowedIDIRRoles: [SA, HQA] }, ADD_AN_LOA: { allowedIDIRRoles: [SA, HQA] }, EDIT_AN_LOA: { allowedIDIRRoles: [SA, HQA] }, VIEW_LOA: { allowedIDIRRoles: ALL_IDIR_ROLES }, diff --git a/frontend/src/features/settings/components/dashboard/ManageSettingsDashboard.tsx b/frontend/src/features/settings/components/dashboard/ManageSettingsDashboard.tsx index 6612c3e58..40cb94dcb 100644 --- a/frontend/src/features/settings/components/dashboard/ManageSettingsDashboard.tsx +++ b/frontend/src/features/settings/components/dashboard/ManageSettingsDashboard.tsx @@ -6,11 +6,7 @@ import { SETTINGS_TABS, SettingsTab } from "../../types/tabs"; import OnRouteBCContext from "../../../../common/authentication/OnRouteBCContext"; import { ERROR_ROUTES } from "../../../../routes/constants"; import { SpecialAuthorizations } from "../../pages/SpecialAuthorizations/SpecialAuthorizations"; -import { useFeatureFlagsQuery } from "../../../../common/hooks/hooks"; -import { - canViewSpecialAuthorizations, - canViewSuspend, -} from "../../helpers/permissions"; +import { canViewSuspend } from "../../helpers/permissions"; import { CreditAccountMetadataComponent } from "../../pages/CreditAccountMetadataComponent"; import { usePermissionMatrix } from "../../../../common/authentication/PermissionMatrix"; import { useGetCreditAccountMetadataQuery } from "../../hooks/creditAccount"; @@ -20,8 +16,6 @@ import { CREDIT_ACCOUNT_USER_TYPE } from "../../types/creditAccount"; export const ManageSettingsDashboard = React.memo(() => { const { userClaims, companyId, idirUserDetails } = useContext(OnRouteBCContext); - - const { data: featureFlags } = useFeatureFlagsQuery(); const { data: creditAccountMetadata, isPending } = useGetCreditAccountMetadataQuery(companyId as number); @@ -41,15 +35,17 @@ export const ManageSettingsDashboard = React.memo(() => { } }; - const isStaffActingAsCompany = Boolean(idirUserDetails?.userRole); const isFinanceUser = idirUserDetails?.userRole === IDIR_USER_ROLE.FINANCE; const [hideSuspendTab, setHideSuspendTab] = useState(false); const showSuspendTab = canViewSuspend(userClaims) && !hideSuspendTab; - const showSpecialAuth = - isStaffActingAsCompany && - canViewSpecialAuthorizations(userClaims, idirUserDetails?.userRole) && - featureFlags?.["LOA"] === "ENABLED"; + + const showSpecialAuth = usePermissionMatrix({ + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "VIEW_SPECIAL_AUTHORIZATIONS", + }, + }); const showCreditAccountTab = usePermissionMatrix({ featureFlag: "CREDIT-ACCOUNT", diff --git a/frontend/src/features/settings/hooks/specialAuthorizations.ts b/frontend/src/features/settings/hooks/specialAuthorizations.ts index 98a8fe207..404486ce6 100644 --- a/frontend/src/features/settings/hooks/specialAuthorizations.ts +++ b/frontend/src/features/settings/hooks/specialAuthorizations.ts @@ -13,6 +13,7 @@ const QUERY_KEYS = { */ export const useFetchSpecialAuthorizations = ( companyId: number | string, + enabled: boolean = true, ) => { return useQuery({ queryKey: QUERY_KEYS.SPECIAL_AUTH(companyId), @@ -22,7 +23,7 @@ export const useFetchSpecialAuthorizations = ( retry: false, refetchOnMount: "always", refetchOnWindowFocus: false, - enabled: Boolean(companyId), + enabled, }); }; diff --git a/frontend/src/features/settings/pages/SpecialAuthorizations/SpecialAuthorizations.tsx b/frontend/src/features/settings/pages/SpecialAuthorizations/SpecialAuthorizations.tsx index 042ba3f2c..959e2d811 100644 --- a/frontend/src/features/settings/pages/SpecialAuthorizations/SpecialAuthorizations.tsx +++ b/frontend/src/features/settings/pages/SpecialAuthorizations/SpecialAuthorizations.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from "react"; +import { useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { Button } from "@mui/material"; @@ -11,10 +11,12 @@ import { ExpiredLOAModal } from "../../components/SpecialAuthorizations/LOA/expi import { DeleteConfirmationDialog } from "../../../../common/components/dialog/DeleteConfirmationDialog"; import { LOASteps } from "./LOA/LOASteps"; import { useFetchLOAs, useRemoveLOAMutation } from "../../hooks/LOA"; -import { getDefaultNullableVal, getDefaultRequiredVal } from "../../../../common/helpers/util"; -import { DEFAULT_NO_FEE_PERMIT_TYPE, NoFeePermitType } from "../../types/SpecialAuthorization"; +import { getDefaultRequiredVal } from "../../../../common/helpers/util"; +import { + DEFAULT_NO_FEE_PERMIT_TYPE, + NoFeePermitType, +} from "../../types/SpecialAuthorization"; import { NoFeePermitsSection } from "../../components/SpecialAuthorizations/NoFeePermits/NoFeePermitsSection"; -import OnRouteBCContext from "../../../../common/authentication/OnRouteBCContext"; import { LCVSection } from "../../components/SpecialAuthorizations/LCV/LCVSection"; import { downloadLOA } from "../../apiManager/loa"; import { @@ -22,69 +24,81 @@ import { useUpdateLCV, useUpdateNoFee, } from "../../hooks/specialAuthorizations"; +import { usePermissionMatrix } from "../../../../common/authentication/PermissionMatrix"; +import { useFeatureFlagsQuery } from "../../../../common/hooks/hooks"; -import { - canUpdateLCVFlag, - canUpdateLOA, - canUpdateNoFeePermitsFlag, - canViewLCVFlag, - canViewLOA, - canViewNoFeePermitsFlag, -} from "../../helpers/permissions"; - -export const SpecialAuthorizations = ({ - companyId, -}: { - companyId: number; -}) => { - const { - data: specialAuthorizations, - refetch: refetchSpecialAuth, - } = useFetchSpecialAuthorizations(companyId); - - const noFeeType = getDefaultRequiredVal(null, specialAuthorizations?.noFeeType); - const isLcvAllowed = getDefaultRequiredVal(false, specialAuthorizations?.isLcvAllowed); +export const SpecialAuthorizations = ({ companyId }: { companyId: number }) => { + const { data: featureFlags } = useFeatureFlagsQuery(); + const { data: specialAuthorizations, refetch: refetchSpecialAuth } = + useFetchSpecialAuthorizations( + companyId, + // At least one of the special auth feature flags must be enabled + // to decide whether to enable the query. + featureFlags?.["NO-FEE"] === "ENABLED" || + featureFlags?.["LCV"] === "ENABLED", + ); + + const noFeeType = getDefaultRequiredVal( + null, + specialAuthorizations?.noFeeType, + ); + const isLcvAllowed = getDefaultRequiredVal( + false, + specialAuthorizations?.isLcvAllowed, + ); const [showExpiredLOAs, setShowExpiredLOAs] = useState(false); const [loaToDelete, setLoaToDelete] = useState>(null); const [showLOASteps, setShowLOASteps] = useState(false); const [loaToEdit, setLoaToEdit] = useState>(null); - const { - userClaims, - idirUserDetails, - userDetails, - } = useContext(OnRouteBCContext); - - const canEditNoFeePermits = canUpdateNoFeePermitsFlag( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); - - const canViewNoFeePermits = canViewNoFeePermitsFlag( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); - - const canUpdateLCV = canUpdateLCVFlag( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); - - const canViewLCV = canViewLCVFlag( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); - - const canWriteLOA = canUpdateLOA( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); - - const canReadLOA = canViewLOA( - userClaims, - getDefaultNullableVal(idirUserDetails?.userRole, userDetails?.userRole), - ); + const canEditNoFeePermits = usePermissionMatrix({ + featureFlag: "NO-FEE", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "UPDATE_NO_FEE_FLAG", + }, + }); + + const canViewNoFeePermits = usePermissionMatrix({ + featureFlag: "NO-FEE", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "VIEW_SPECIAL_AUTHORIZATIONS", + }, + }); + + const canUpdateLCV = usePermissionMatrix({ + featureFlag: "LCV", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "REMOVE_LCV_FLAG", + }, + }); + + const canViewLCV = usePermissionMatrix({ + featureFlag: "LCV", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "VIEW_SPECIAL_AUTHORIZATIONS", + }, + }); + + const canReadLOA = usePermissionMatrix({ + featureFlag: "LOA", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "VIEW_LOA", + }, + }); + + const canWriteLOA = usePermissionMatrix({ + featureFlag: "LOA", + permissionMatrixKeys: { + permissionMatrixFeatureKey: "MANAGE_SETTINGS", + permissionMatrixFunctionKey: "EDIT_AN_LOA", + }, + }); const updateNoFeeMutation = useUpdateNoFee(); const updateLCVMutation = useUpdateLCV(); diff --git a/vehicles/src/modules/special-auth/special-auth.controller.ts b/vehicles/src/modules/special-auth/special-auth.controller.ts index 9bd7e4b23..f4197ebc9 100644 --- a/vehicles/src/modules/special-auth/special-auth.controller.ts +++ b/vehicles/src/modules/special-auth/special-auth.controller.ts @@ -27,7 +27,6 @@ import { @ApiBearerAuth() @ApiTags('Special Authorization') -@IsFeatureFlagEnabled('LOA') @Controller('companies/:companyId/special-auths') @ApiMethodNotAllowedResponse({ description: 'The Special Authorizaion Api Method Not Allowed Response', @@ -79,6 +78,7 @@ export class SpecialAuthController { ], }) @Put('/lcv') + @IsFeatureFlagEnabled('LCV') async updateLcv( @Req() request: Request, @Param() { companyId }: CompanyIdPathParamDto, @@ -107,6 +107,7 @@ export class SpecialAuthController { IDIRUserRole.SYSTEM_ADMINISTRATOR, ], }) + @IsFeatureFlagEnabled('NO-FEE') @Put('/no-fee') async updateNoFee( @Req() request: Request,