From c7ad42cf272c2c9873b31fecb3303a72e2540f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81sd=C3=ADs=20Erna=20Gu=C3=B0mundsd=C3=B3ttir?= Date: Mon, 7 Oct 2024 14:20:12 +0000 Subject: [PATCH] feat(service-portal): feature flag resolver for documents (#16285) * fix: def info and alert * feat: add feature flag to resolver * fix: move ff call to seperate function --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/screens/Dashboard/Dashboard.tsx | 10 +- .../documents/src/lib/document.module.ts | 3 +- .../documents/src/lib/documentV2.resolver.ts | 36 ++++-- .../documents/src/lib/documentV2.service.ts | 108 ++++++++++++++++++ .../lib/models/v2/documentContent.model.ts | 5 +- libs/feature-flags/src/lib/features.ts | 2 +- .../DocumentLine/DocumentLineV3.tsx | 4 +- .../OverviewDisplay/DesktopOverviewV3.tsx | 4 +- .../documents/src/hooks/useDocumentListV3.ts | 2 +- .../src/screens/Overview/OverviewV3.tsx | 4 +- 10 files changed, 154 insertions(+), 24 deletions(-) diff --git a/apps/service-portal/src/screens/Dashboard/Dashboard.tsx b/apps/service-portal/src/screens/Dashboard/Dashboard.tsx index bd5f5a9b7c2e..4dcec472ff69 100644 --- a/apps/service-portal/src/screens/Dashboard/Dashboard.tsx +++ b/apps/service-portal/src/screens/Dashboard/Dashboard.tsx @@ -17,7 +17,7 @@ import { DocumentsPaths, DocumentLine, DocumentLineV3, - useDocumentList, + useDocumentListV3, } from '@island.is/service-portal/documents' import { LinkResolver, @@ -41,9 +41,7 @@ import { useFeatureFlagClient } from '@island.is/react/feature-flags' export const Dashboard: FC> = () => { const { userInfo } = useAuth() - const { filteredDocuments, data, loading } = useDocumentList({ - defaultPageSize: 8, - }) + const { data: organizations } = useOrganizations() const { formatMessage } = useLocale() const { width } = useWindowSize() @@ -56,6 +54,10 @@ export const Dashboard: FC> = () => { // Versioning feature flag. Remove after feature is live. const [v3Enabled, setV3Enabled] = useState() + const { filteredDocuments, data, loading } = useDocumentListV3({ + defaultPageSize: 8, + }) + const featureFlagClient = useFeatureFlagClient() useEffect(() => { const isFlagEnabled = async () => { diff --git a/libs/api/domains/documents/src/lib/document.module.ts b/libs/api/domains/documents/src/lib/document.module.ts index ec76a2873ae2..59c7788956ca 100644 --- a/libs/api/domains/documents/src/lib/document.module.ts +++ b/libs/api/domains/documents/src/lib/document.module.ts @@ -6,9 +6,10 @@ import { DocumentServiceV2 } from './documentV2.service' import { DocumentResolverV1 } from './documentV1.resolver' import { DocumentResolverV2 } from './documentV2.resolver' import { DocumentService } from './documentV1.service' +import { FeatureFlagModule } from '@island.is/nest/feature-flags' @Module({ - imports: [DocumentsClientV2Module, DocumentsClientModule], + imports: [DocumentsClientV2Module, DocumentsClientModule, FeatureFlagModule], providers: [ DocumentResolverV2, DocumentResolverV1, diff --git a/libs/api/domains/documents/src/lib/documentV2.resolver.ts b/libs/api/domains/documents/src/lib/documentV2.resolver.ts index 3622993197f9..221a2e87225f 100644 --- a/libs/api/domains/documents/src/lib/documentV2.resolver.ts +++ b/libs/api/domains/documents/src/lib/documentV2.resolver.ts @@ -16,7 +16,11 @@ import { Document as DocumentV2, PaginatedDocuments, } from './models/v2/document.model' - +import { + FeatureFlagGuard, + FeatureFlagService, + Features, +} from '@island.is/nest/feature-flags' import { PostRequestPaperInput } from './dto/postRequestPaperInput' import { DocumentInput } from './models/v2/document.input' import { DocumentServiceV2 } from './documentV2.service' @@ -35,13 +39,14 @@ import { DocumentConfirmActions } from './models/v2/confirmActions.model' const LOG_CATEGORY = 'documents-resolver' -@UseGuards(IdsUserGuard, ScopesGuard) +@UseGuards(IdsUserGuard, ScopesGuard, FeatureFlagGuard) @Resolver(() => PaginatedDocuments) @Audit({ namespace: '@island.is/api/document-v2' }) export class DocumentResolverV2 { constructor( private documentServiceV2: DocumentServiceV2, private readonly auditService: AuditService, + private readonly featureFlagService: FeatureFlagService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -53,6 +58,7 @@ export class DocumentResolverV2 { locale: Locale = 'is', @CurrentUser() user: User, ): Promise { + const ffEnabled = await this.getFeatureFlag() try { return await this.auditService.auditPromise( { @@ -62,12 +68,14 @@ export class DocumentResolverV2 { resources: input.id, meta: { includeDocument: input.includeDocument }, }, - this.documentServiceV2.findDocumentById( - user.nationalId, - input.id, - locale, - input.includeDocument, - ), + ffEnabled + ? this.documentServiceV2.findDocumentByIdV3( + user.nationalId, + input.id, + locale, + input.includeDocument, + ) + : this.documentServiceV2.findDocumentById(user.nationalId, input.id), ) } catch (e) { this.logger.info('failed to get single document', { @@ -82,10 +90,13 @@ export class DocumentResolverV2 { @Scopes(DocumentsScope.main) @Query(() => PaginatedDocuments, { nullable: true }) @Audit() - documentsV2( + async documentsV2( @Args('input') input: DocumentsInput, @CurrentUser() user: User, ): Promise { + const ffEnabled = await this.getFeatureFlag() + if (ffEnabled) + return this.documentServiceV2.listDocumentsV3(user.nationalId, input) return this.documentServiceV2.listDocuments(user.nationalId, input) } @@ -206,4 +217,11 @@ export class DocumentResolverV2 { throw e } } + + private async getFeatureFlag(): Promise { + return await this.featureFlagService.getValue( + Features.isServicePortalDocumentsV3PageEnabled, + false, + ) + } } diff --git a/libs/api/domains/documents/src/lib/documentV2.service.ts b/libs/api/domains/documents/src/lib/documentV2.service.ts index aaf967fc8dbe..e22ced843676 100644 --- a/libs/api/domains/documents/src/lib/documentV2.service.ts +++ b/libs/api/domains/documents/src/lib/documentV2.service.ts @@ -36,6 +36,51 @@ export class DocumentServiceV2 { async findDocumentById( nationalId: string, documentId: string, + ): Promise { + const document = await this.documentService.getCustomersDocument( + nationalId, + documentId, + ) + + if (!document) { + return null // Null document logged in clients-documents-v2 + } + + let type: FileType + switch (document.fileType) { + case 'html': + type = FileType.HTML + break + case 'pdf': + type = FileType.PDF + break + case 'url': + type = FileType.URL + break + default: + type = FileType.UNKNOWN + } + + return { + ...document, + publicationDate: document.date, + id: documentId, + name: document.fileName, + downloadUrl: `${this.downloadServiceConfig.baseUrl}/download/v1/electronic-documents/${documentId}`, + sender: { + id: document.senderNationalId, + name: document.senderName, + }, + content: { + type, + value: document.content, + }, + } + } + + async findDocumentByIdV3( + nationalId: string, + documentId: string, locale?: string, includeDocument?: boolean, ): Promise { @@ -123,6 +168,69 @@ export class DocumentServiceV2 { nationalId, }) + if (typeof documents?.totalCount !== 'number') { + this.logger.warn('Document total count unavailable', { + category: LOG_CATEGORY, + totalCount: documents?.totalCount, + }) + } + + const documentData: Array = + documents?.documents + .map((d) => { + if (!d) { + return null + } + + return { + ...d, + id: d.id, + downloadUrl: `${this.downloadServiceConfig.baseUrl}/download/v1/electronic-documents/${d.id}`, + sender: { + name: d.senderName, + id: d.senderNationalId, + }, + } + }) + .filter(isDefined) ?? [] + + return { + data: documentData, + totalCount: documents?.totalCount ?? 0, + unreadCount: documents?.unreadCount, + pageInfo: { + hasNextPage: false, + }, + } + } + + async listDocumentsV3( + nationalId: string, + input: DocumentsInput, + ): Promise { + //If a delegated user is viewing the mailbox, do not return any health related data + //Category is now "1,2,3,...,n" + const { categoryIds, ...restOfInput } = input + let mutableCategoryIds = categoryIds ?? [] + + if (input.isLegalGuardian) { + if (!mutableCategoryIds.length) { + mutableCategoryIds = (await this.getCategories(nationalId, true)).map( + (c) => c.id, + ) + } else { + mutableCategoryIds = mutableCategoryIds.filter( + (c) => c === HEALTH_CATEGORY_ID, + ) + } + } + + const documents = await this.documentService.getDocumentList({ + ...restOfInput, + categoryId: mutableCategoryIds.join(), + nationalId, + }) + if (typeof documents?.totalCount !== 'number') { this.logger.warn('Document total count unavailable', { category: LOG_CATEGORY, diff --git a/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts b/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts index fac02463df0b..2fbf5c02dd09 100644 --- a/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts +++ b/libs/api/domains/documents/src/lib/models/v2/documentContent.model.ts @@ -14,8 +14,9 @@ export class DocumentContent { @Field(() => FileType) type!: FileType - @Field({ + @Field(() => String, { description: 'Either pdf base64 string, html markup string, or an url', + nullable: true, }) - value!: string + value?: string | null } diff --git a/libs/feature-flags/src/lib/features.ts b/libs/feature-flags/src/lib/features.ts index 0749442f1775..c3b1d9d48cb2 100644 --- a/libs/feature-flags/src/lib/features.ts +++ b/libs/feature-flags/src/lib/features.ts @@ -57,7 +57,7 @@ export enum Features { ServicePortalNotificationsEnabled = 'isServicePortalNotificationsPageEnabled', servicePortalLawAndOrderModuleEnabled = 'isServicePortalLawAndOrderModuleEnabled', servicePortalDocumentsActionsEnabled = 'isServicePortalDocumentsActionsEnabled', - + isServicePortalDocumentsV3PageEnabled = 'isServicePortalDocumentsV3PageEnabled', //Occupational License Health directorate fetch enabled occupationalLicensesHealthDirectorate = 'isHealthDirectorateOccupationalLicenseEnabled', diff --git a/libs/service-portal/documents/src/components/DocumentLine/DocumentLineV3.tsx b/libs/service-portal/documents/src/components/DocumentLine/DocumentLineV3.tsx index 394d1be3a6dd..bb3fc311b048 100644 --- a/libs/service-portal/documents/src/components/DocumentLine/DocumentLineV3.tsx +++ b/libs/service-portal/documents/src/components/DocumentLine/DocumentLineV3.tsx @@ -17,7 +17,7 @@ import { useNavigate, useParams, } from 'react-router-dom' -import { useDocumentList } from '../../hooks/useDocumentListV3' +import { useDocumentListV3 } from '../../hooks/useDocumentListV3' import { useIsChildFocusedorHovered } from '../../hooks/useIsChildFocused' import { useMailAction } from '../../hooks/useMailActionV2' import { DocumentsPaths } from '../../lib/paths' @@ -75,7 +75,7 @@ export const DocumentLineV3: FC = ({ bookmarkSuccess, } = useMailAction() - const { fetchObject, refetch } = useDocumentList() + const { fetchObject, refetch } = useDocumentListV3() const { setActiveDocument, diff --git a/libs/service-portal/documents/src/components/OverviewDisplay/DesktopOverviewV3.tsx b/libs/service-portal/documents/src/components/OverviewDisplay/DesktopOverviewV3.tsx index d6abdd655eb5..a618dead8479 100644 --- a/libs/service-portal/documents/src/components/OverviewDisplay/DesktopOverviewV3.tsx +++ b/libs/service-portal/documents/src/components/OverviewDisplay/DesktopOverviewV3.tsx @@ -9,7 +9,7 @@ import NoPDF from '../NoPDF/NoPDF' import { SERVICE_PORTAL_HEADER_HEIGHT_LG } from '@island.is/service-portal/constants' import { useDocumentContext } from '../../screens/Overview/DocumentContext' import * as styles from './OverviewDisplay.css' -import { useDocumentList } from '../../hooks/useDocumentListV3' +import { useDocumentListV3 } from '../../hooks/useDocumentListV3' interface Props { activeBookmark: boolean @@ -25,7 +25,7 @@ export const DesktopOverview: FC = ({ useNamespaces('sp.documents') const { formatMessage } = useLocale() const { activeDocument } = useDocumentContext() - const { activeArchive } = useDocumentList() + const { activeArchive } = useDocumentListV3() if (loading) { return ( diff --git a/libs/service-portal/documents/src/hooks/useDocumentListV3.ts b/libs/service-portal/documents/src/hooks/useDocumentListV3.ts index 6baee903493a..0ffe4e76bf20 100644 --- a/libs/service-portal/documents/src/hooks/useDocumentListV3.ts +++ b/libs/service-portal/documents/src/hooks/useDocumentListV3.ts @@ -9,7 +9,7 @@ export const pageSize = 10 type UseDocumentListProps = { defaultPageSize?: number } -export const useDocumentList = (props?: UseDocumentListProps) => { +export const useDocumentListV3 = (props?: UseDocumentListProps) => { const { filterValue, page, diff --git a/libs/service-portal/documents/src/screens/Overview/OverviewV3.tsx b/libs/service-portal/documents/src/screens/Overview/OverviewV3.tsx index 806ff02558f9..427be66fccdc 100644 --- a/libs/service-portal/documents/src/screens/Overview/OverviewV3.tsx +++ b/libs/service-portal/documents/src/screens/Overview/OverviewV3.tsx @@ -21,7 +21,7 @@ import DocumentLine from '../../components/DocumentLine/DocumentLineV3' import { FavAndStashV3 } from '../../components/FavAndStash/FavAndStashV3' import DocumentDisplay from '../../components/OverviewDisplay/OverviewDocumentDisplayV3' import { useDocumentFilters } from '../../hooks/useDocumentFilters' -import { pageSize, useDocumentList } from '../../hooks/useDocumentListV3' +import { pageSize, useDocumentListV3 } from '../../hooks/useDocumentListV3' import { useKeyDown } from '../../hooks/useKeyDown' import { useMailAction } from '../../hooks/useMailActionV2' import { DocumentsPaths } from '../../lib/paths' @@ -58,7 +58,7 @@ export const ServicePortalDocumentsV3 = () => { totalPages, filteredDocuments, totalCount, - } = useDocumentList() + } = useDocumentListV3() const { handlePageChange, handleSearchChange } = useDocumentFilters()