Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(j-s): Zip all files for defenders in indictment cases #17716

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

oddsson
Copy link
Member

@oddsson oddsson commented Jan 29, 2025

Zip all files for defenders in indictment cases

Asana
Asana

What

In R-cases, we offer defenders to download all case files as a ZIP file. This PR enables that for S-cases as well. If the case files are not shared with the defender, only the ruling and court record will be included. If they are, all case files visible to the defender will be included.

This PR also implements a minor change in the names of the files in the ZIP file. They now are capitalized and include Icelandic letters.

Why

So that defender users don't have to open each individual file to print out all the case files.

Screenshots / Gifs

Screenshot 2025-01-29 at 14 02 55

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • Formatting passes locally with my changes
  • I have rebased against main before asking for a review

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a ZipButton component for downloading all case documents.
    • Enhanced file download capabilities for indictment and request cases.
  • Improvements

    • Expanded file retrieval methods to support additional case types, including indictment cases.
    • Optimized file fetching process with concurrent AWS S3 file retrieval.
    • Improved handling of case file categories for defenders.
  • User Experience

    • Simplified document download process for judicial system users.
    • Removed the previous button for downloading documents, streamlining the interface.

@oddsson oddsson requested a review from a team as a code owner January 29, 2025 14:03
Copy link
Contributor

coderabbitai bot commented Jan 29, 2025

Walkthrough

This pull request introduces modifications to the judicial system's backend and frontend, specifically enhancing file access and download capabilities for various case types. Key changes include the addition of indictmentCases to the CaseTypeGuard in the LimitedAccessCaseController, updates to the LimitedAccessCaseService for improved file handling, and the introduction of a new ZipButton component for downloading case files. These updates broaden the eligibility for file retrieval and streamline user interactions with case documents.

Changes

File Change Summary
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts Added indictmentCases to CaseTypeGuard for getAllFilesZip method
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts Introduced new getFileCategories method, enhanced getAllFilesZip to handle different case types more efficiently
apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts Exported constants for defender case file categories for indictment cases
apps/judicial-system/web/src/components/ZipButton/ZipButton.tsx New React component for downloading case files as a zip
apps/judicial-system/web/src/components/ZipButton/ZipButton.strings.ts New strings file for internationalization of ZipButton messages
apps/judicial-system/web/src/routes/Defender/CaseOverview.tsx Replaced existing download button with new ZipButton component
apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx Added ZipButton for downloading files in indictment cases
apps/judicial-system/web/src/components/index.ts Added export for new ZipButton component
apps/judicial-system/web/src/routes/Defender/CaseOverview.strings.ts Removed getAllDocuments message definition
apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getAllFilesZipGuards.spec.ts Updated guard configuration to include indictmentCases

Possibly related PRs

  • fix(j-s): Service Certificate #16566: This PR introduces changes to the handling of service certificates, which could be related to the main PR's modifications to the limited access case controller and service.
  • fix(j-s): Merge Case Access #16786: This PR focuses on improving access permissions for defenders and spokespersons in merged cases, which could be relevant to the main PR's changes.

Suggested labels

automerge

Suggested reviewers

  • thorhildurt
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
apps/judicial-system/web/src/components/ZipButton/ZipButton.tsx (2)

22-22: Improve download filename handling.

The filename should handle cases where courtCaseNumber is null and include proper file extension.

Apply this diff to improve the filename handling:

-      download={`mal_${courtCaseNumber}`}
+      download={`mal_${courtCaseNumber || 'unknown'}.zip`}

25-27: Add loading state to prevent multiple clicks.

The button should show a loading state while the download is in progress to prevent multiple clicks and provide better user feedback.

Apply this diff to add loading state:

+import { useState } from 'react'
+
 const ZipButton: FC<Props> = (props) => {
   const { formatMessage } = useIntl()
   const { caseId, courtCaseNumber } = props
+  const [isLoading, setIsLoading] = useState(false)
+
+  const handleClick = () => {
+    setIsLoading(true)
+  }

   return (
     <a
       href={`${api.apiUrl}/api/case/${encodeURIComponent(caseId)}/limitedAccess/allFiles`}
       download={`mal_${courtCaseNumber || 'unknown'}.zip`}
       className={styles.downloadAllButton}
+      onClick={handleClick}
     >
-      <Button variant="ghost" size="small" icon="download" iconType="outline">
+      <Button
+        variant="ghost"
+        size="small"
+        icon="download"
+        iconType="outline"
+        loading={isLoading}
+      >
         {formatMessage(strings.getAllDocuments)}
       </Button>
     </a>
apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts (1)

Line range hint 33-41: Consider using array spread for better readability.

While concat works correctly, using array spread operator would improve readability.

Apply this diff to use array spread:

-export const defenderCaseFileCategoriesForIndictmentCases =
-  defenderDefaultCaseFileCategoriesForIndictmentCases.concat(
-    CaseFileCategory.CRIMINAL_RECORD,
-    CaseFileCategory.COST_BREAKDOWN,
-    CaseFileCategory.CASE_FILE,
-    CaseFileCategory.PROSECUTOR_CASE_FILE,
-    CaseFileCategory.DEFENDANT_CASE_FILE,
-    CaseFileCategory.CIVIL_CLAIM,
-  )
+export const defenderCaseFileCategoriesForIndictmentCases = [
+  ...defenderDefaultCaseFileCategoriesForIndictmentCases,
+  CaseFileCategory.CRIMINAL_RECORD,
+  CaseFileCategory.COST_BREAKDOWN,
+  CaseFileCategory.CASE_FILE,
+  CaseFileCategory.PROSECUTOR_CASE_FILE,
+  CaseFileCategory.DEFENDANT_CASE_FILE,
+  CaseFileCategory.CIVIL_CLAIM,
+]
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)

571-574: Use optional chaining for safer property access.

The nested property access can be simplified using optional chaining.

-      const defendant = theCase.defendants && theCase.defendants[0]
-      const subpoena =
-        defendant && defendant.subpoenas && defendant.subpoenas[0]
+      const defendant = theCase.defendants?.[0]
+      const subpoena = defendant?.subpoenas?.[0]
🧰 Tools
🪛 Biome (1.9.4)

[error] 571-572: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b76676 and 54fd9e9.

📒 Files selected for processing (10)
  • apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts (1 hunks)
  • apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (4 hunks)
  • apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts (1 hunks)
  • apps/judicial-system/backend/src/app/modules/file/index.ts (1 hunks)
  • apps/judicial-system/web/src/components/ZipButton/ZipButton.strings.ts (1 hunks)
  • apps/judicial-system/web/src/components/ZipButton/ZipButton.tsx (1 hunks)
  • apps/judicial-system/web/src/components/index.ts (1 hunks)
  • apps/judicial-system/web/src/routes/Defender/CaseOverview.strings.ts (0 hunks)
  • apps/judicial-system/web/src/routes/Defender/CaseOverview.tsx (5 hunks)
  • apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • apps/judicial-system/web/src/routes/Defender/CaseOverview.strings.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/judicial-system/web/src/components/ZipButton/ZipButton.strings.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/judicial-system/web/src/components/index.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/backend/src/app/modules/file/index.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/web/src/components/ZipButton/ZipButton.tsx (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/web/src/routes/Defender/CaseOverview.tsx (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
📓 Learnings (2)
apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx (1)
Learnt from: oddsson
PR: island-is/island.is#16731
File: apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx:172-186
Timestamp: 2024-11-12T15:15:20.157Z
Learning: In `IndictmentOverview.tsx`, since the defendants data does not change, using `useMemo` to memoize the filtering logic is unnecessary.
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts (1)
Learnt from: gudjong
PR: island-is/island.is#16389
File: apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getIndictmentPdfGuards.spec.ts:24-25
Timestamp: 2024-11-12T15:15:11.835Z
Learning: In certain scenarios within the judicial-system backend, the `RolesGuard` may intentionally follow the `CaseExistsGuard` when specific roles rules require the guard order to be reversed, as seen in tests like `getIndictmentPdfGuards.spec.ts`.
🪛 Biome (1.9.4)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts

[error] 571-572: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 573-575: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (10)
apps/judicial-system/backend/src/app/modules/file/index.ts (1)

5-7: LGTM! The exports align with the PR objectives.

The new exports enable the implementation of ZIP functionality for indictment cases, maintaining consistent code organization.

apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts (1)

28-31: LGTM! Well-defined default categories.

The default categories for indictment cases are correctly defined and align with the PR objectives.

apps/judicial-system/web/src/components/index.ts (1)

116-116: LGTM! Export follows consistent pattern.

The ZipButton export maintains the established pattern of component exports in the file.

apps/judicial-system/web/src/routes/Defender/CaseOverview.tsx (2)

226-226: Consistent prop value format.

The change from pdfType={'value'} to pdfType="value" improves code consistency and readability.

Also applies to: 234-234, 247-247


259-262: LGTM! ZipButton integration for completed cases.

The ZipButton is correctly placed after other document buttons and properly guarded by the completed case check.

apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx (2)

186-225: Improved layout structure with semantic HTML.

The restructuring enhances readability by:

  • Using semantic section elements
  • Properly organizing conditional rendering
  • Maintaining clear component hierarchy

305-311: LGTM! Proper access control for ZipButton.

The ZipButton is correctly restricted to:

  • Defender users only
  • Completed cases only
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.controller.ts (1)

399-403: LGTM! Extended case type support.

The CaseTypeGuard is correctly updated to include indictment cases, maintaining proper guard ordering as per established patterns.

apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (2)

Line range hint 538-551: Performance improvement using Promise.all.

Good optimization by fetching files concurrently instead of sequentially.

🧰 Tools
🪛 Biome (1.9.4)

[error] 571-572: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 573-575: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


605-615: LGTM! Well-structured file category logic.

The getFileCategories method:

  • Cleanly separates category logic
  • Handles both request and indictment cases
  • Considers file sharing status

Copy link

codecov bot commented Jan 29, 2025

Codecov Report

Attention: Patch coverage is 12.30769% with 57 lines in your changes missing coverage. Please review.

Project coverage is 44.28%. Comparing base (ff4eb6a) to head (3ad8f4b).
Report is 49 commits behind head on main.

Files with missing lines Patch % Lines
...s/Shared/IndictmentOverview/IndictmentOverview.tsx 0.00% 33 Missing ⚠️
.../src/app/modules/case/limitedAccessCase.service.ts 8.69% 21 Missing ⚠️
...-system/web/src/components/ZipButton/ZipButton.tsx 25.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main   #17716       +/-   ##
===========================================
+ Coverage   35.55%   44.28%    +8.73%     
===========================================
  Files        7038     1317     -5721     
  Lines      150799    35585   -115214     
  Branches    43094    11899    -31195     
===========================================
- Hits        53611    15760    -37851     
+ Misses      97188    19825    -77363     
Flag Coverage Δ
air-discount-scheme-backend ?
air-discount-scheme-web ?
api ?
api-catalogue-services ?
api-domains-air-discount-scheme ?
api-domains-assets ?
api-domains-auth-admin ?
api-domains-communications ?
api-domains-criminal-record ?
api-domains-driving-license ?
api-domains-education ?
api-domains-health-insurance ?
api-domains-license-service ?
api-domains-mortgage-certificate ?
api-domains-payment-schedule ?
application-api-files ?
application-core ?
application-system-api ?
application-template-api-modules ?
application-templates-accident-notification ?
application-templates-car-recycling ?
application-templates-criminal-record ?
application-templates-driving-license ?
application-templates-estate ?
application-templates-example-payment ?
application-templates-financial-aid ?
application-templates-general-petition ?
application-templates-health-insurance ?
application-templates-inheritance-report ?
application-templates-marriage-conditions ?
application-templates-mortgage-certificate ?
application-templates-new-primary-school ?
application-templates-parental-leave ?
application-types ?
application-ui-components ?
application-ui-shell ?
auth-admin-web ?
auth-api-lib ?
auth-nest-tools ?
auth-react ?
auth-shared ?
clients-charge-fjs-v2 ?
clients-driving-license ?
clients-driving-license-book ?
clients-financial-statements-inao ?
clients-license-client ?
clients-middlewares ?
clients-regulations ?
clients-rsk-company-registry ?
clients-rsk-personal-tax-return ?
clients-smartsolutions ?
clients-syslumenn ?
clients-zendesk ?
cms ?
cms-translations ?
consultation-portal ?
content-search-index-manager ?
content-search-toolkit ?
contentful-apps ?
dokobit-signing ?
download-service ?
email-service ?
feature-flags ?
file-storage ?
financial-aid-backend ?
financial-aid-shared ?
icelandic-names-registry-backend ?
infra-nest-server ?
infra-tracing ?
island-ui-core ?
judicial-system-api ?
judicial-system-audit-trail ?
judicial-system-backend 55.74% <22.22%> (-0.03%) ⬇️
judicial-system-formatters ?
judicial-system-message ?
judicial-system-message-handler ?
judicial-system-scheduler ?
judicial-system-types ?
license-api ?
localization ?
logging ?
message-queue ?
nest-audit ?
nest-aws ?
nest-config ?
nest-core ?
nest-feature-flags ?
nest-problem ?
nest-sequelize ?
nest-swagger ?
nova-sms ?
portals-admin-regulations-admin ?
portals-core ?
react-spa-shared ?
reference-backend ?
regulations ?
residence-history ?
samradsgatt ?
service-portal-core ?
service-portal-health ?
service-portal-information ?
services-auth-admin-api ?
services-auth-delegation-api ?
services-auth-ids-api ?
services-auth-personal-representative ?
services-auth-personal-representative-public ?
services-auth-public-api ?
services-documents ?
services-endorsements-api ?
services-search-indexer ?
services-sessions ?
services-university-gateway ?
services-user-notification ?
services-user-profile ?
shared-components ?
shared-form-fields ?
shared-mocking ?
shared-pii ?
shared-problem ?
shared-utils ?
skilavottord-ws ?
testing-e2e ?
web ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...c/app/modules/case/limitedAccessCase.controller.ts 97.50% <ø> (ø)
...nd/src/app/modules/file/guards/caseFileCategory.ts 95.74% <100.00%> (ø)
...icial-system/backend/src/app/modules/file/index.ts 100.00% <100.00%> (ø)
.../web/src/components/ZipButton/ZipButton.strings.ts 100.00% <100.00%> (ø)
...em/web/src/routes/Defender/CaseOverview.strings.ts 0.00% <ø> (ø)
...al-system/web/src/routes/Defender/CaseOverview.tsx 0.00% <ø> (ø)
...-system/web/src/components/ZipButton/ZipButton.tsx 25.00% <25.00%> (ø)
.../src/app/modules/case/limitedAccessCase.service.ts 63.35% <8.69%> (-9.21%) ⬇️
...s/Shared/IndictmentOverview/IndictmentOverview.tsx 0.00% <0.00%> (ø)

... and 5894 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 50b7354...3ad8f4b. Read the comment docs.

@@ -519,18 +526,17 @@ export class LimitedAccessCaseService {

async getAllFilesZip(theCase: Case, user: TUser): Promise<Buffer> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question to @gudjong

This PR introduces quite a bit of complexity to this function. This function now decides what case files should be included in the ZIP file based on the type of the case and whether or not the the case files are shared with the defender or not.

I'm a bit afraid that a regression will lead to files being included in the ZIP file that should not be included and users will be able to see files that they are not supposed to.

Would you / how would you add tests for this function? Would you write test for this function in the service or would you test the controller? Can we maybe pair on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great questions. I am not sure what would be the best way to test but lean towards testing the controller. I´ll take a look at the code and pairing the tests sounds good.

Copy link
Member

@gudjong gudjong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good. I have one change request regarding multiple case files records and one suggestion to support multiple defendants right away so we do not need to revisit this code when prosecutors start sending us indictments with multiple defendants.

I am also wondering if we shouldn't also include files from merged cases. The defenders can access from the client and including them in the ZIP file sounds prudent.

Copy link

nx-cloud bot commented Jan 31, 2025

View your CI Pipeline Execution ↗ for commit 7e809e8.

Command Status Duration Result
nx run-many --projects judicial-system-backend ... ✅ Succeeded 14m 24s View ↗
nx run-many --target=build --projects=judicial-... ✅ Succeeded 2m 31s View ↗
nx run-many --projects judicial-system-web --ta... ✅ Succeeded 1m 13s View ↗
nx run-many --target=codegen/frontend-client --... ✅ Succeeded 12s View ↗
nx run-many --target=codegen/backend-schema --a... ✅ Succeeded 20s View ↗
nx run-many --target=build --projects=judicial-... ✅ Succeeded 16s View ↗
nx run-many --target=lint --projects=judicial-s... ✅ Succeeded 8s View ↗

☁️ Nx Cloud last updated this comment at 2025-01-31 12:45:44 UTC

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)

Line range hint 530-552: Consider implementing error recovery for failed file fetches.

While the current implementation logs warnings for failed file fetches, it might be worth considering whether to:

  1. Notify the user about failed files
  2. Implement retry logic for transient failures
  3. Add file fetch timeout handling
 const fileFetchPromises = caseFilesByCategory.map((file) => {
+  const timeout = 30000; // 30 seconds
+  const fetchWithTimeout = Promise.race([
     this.awsS3Service
       .getObject(theCase.type, file.key)
-      .then((content) => filesToZip.push({ data: content, name: file.name }))
+      .then((content) => {
+        filesToZip.push({ data: content, name: file.name });
+        return { success: true, file: file.name };
+      })
       .catch((reason) => {
         // Tolerate failure, but log what happened
         this.logger.warn(
           `Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
           { reason },
-        ),
+        );
+        return { success: false, file: file.name, error: reason };
+      }),
+    new Promise((_, reject) =>
+      setTimeout(() => reject(new Error(`Timeout fetching ${file.name}`)), timeout)
+    ),
+  ]);
+  return fetchWithTimeout;
 })
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ad8f4b and d616a6d.

📒 Files selected for processing (2)
  • apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (4 hunks)
  • apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/judicial-system/backend/src/app/modules/file/guards/caseFileCategory.ts
🧰 Additional context used
📓 Path-based instructions (1)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🪛 Biome (1.9.4)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts

[error] 572-573: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 574-576: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: tests (judicial-system-backend)
🔇 Additional comments (3)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (3)

28-29: LGTM! Clean import organization.

The new imports are well-organized and clearly indicate the added functionality for handling different case types.

Also applies to: 44-49


582-586: 🛠️ Refactor suggestion

Handle all police case numbers as noted in past reviews.

As mentioned in past reviews, we need to handle all police case numbers since there's a separate case files record for each one.

-data: await this.pdfService.getCaseFilesRecordPdf(
-  theCase,
-  theCase.policeCaseNumbers[0],
-),
-name: `Skjalaskrá ${theCase.courtCaseNumber}.pdf`,
+...(await Promise.all(theCase.policeCaseNumbers.map(async (policeCaseNumber) => ({
+  data: await this.pdfService.getCaseFilesRecordPdf(
+    theCase,
+    policeCaseNumber,
+  ),
+  name: `Skjalaskrá ${policeCaseNumber}.pdf`,
+})))),

Likely invalid or redundant comment.


606-616: LGTM! Clean separation of file category logic.

The getFileCategories method effectively encapsulates the logic for determining which file categories should be included based on case type and sharing status.

However, consider adding unit tests as suggested in past reviews to ensure the security of file access controls.

@oddsson oddsson requested a review from gudjong January 31, 2025 12:22
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/judicial-system/web/src/utils/utils.ts (1)

148-170: Consider breaking down complex boolean logic into smaller functions.

The function combines multiple complex conditions. Consider extracting the defendant and civil claimant checks into separate helper functions for better maintainability and testability.

+const isEligibleDefender = (defendant: Defendant, userNationalId?: string) =>
+  defendant.isDefenderChoiceConfirmed &&
+  defendant.caseFilesSharedWithDefender &&
+  defendant.defenderNationalId &&
+  normalizeAndFormatNationalId(userNationalId).includes(
+    defendant.defenderNationalId,
+  )

+const isEligibleCivilClaimant = (civilClaimant: CivilClaimant, userNationalId?: string) =>
+  civilClaimant.hasSpokesperson &&
+  civilClaimant.isSpokespersonConfirmed &&
+  civilClaimant.caseFilesSharedWithSpokesperson &&
+  civilClaimant.spokespersonNationalId &&
+  normalizeAndFormatNationalId(userNationalId).includes(
+    civilClaimant.spokespersonNationalId,
+  )

 export const shouldDisplayGeneratedPdfFiles = (theCase: Case, user?: User) =>
   Boolean(
     isProsecutionUser(user) ||
-      theCase.defendants?.some(
-        (defendant) =>
-          defendant.isDefenderChoiceConfirmed &&
-          defendant.caseFilesSharedWithDefender &&
-          defendant.defenderNationalId &&
-          normalizeAndFormatNationalId(user?.nationalId).includes(
-            defendant.defenderNationalId,
-          ),
-      ) ||
-      theCase.civilClaimants?.some(
-        (civilClaimant) =>
-          civilClaimant.hasSpokesperson &&
-          civilClaimant.isSpokespersonConfirmed &&
-          civilClaimant.caseFilesSharedWithSpokesperson &&
-          civilClaimant.spokespersonNationalId &&
-          normalizeAndFormatNationalId(user?.nationalId).includes(
-            civilClaimant.spokespersonNationalId,
-          ),
-      ),
+      theCase.defendants?.some((defendant) => 
+        isEligibleDefender(defendant, user?.nationalId)
+      ) ||
+      theCase.civilClaimants?.some((civilClaimant) => 
+        isEligibleCivilClaimant(civilClaimant, user?.nationalId)
+      ),
   )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d616a6d and 7e809e8.

📒 Files selected for processing (3)
  • apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (3 hunks)
  • apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx (4 hunks)
  • apps/judicial-system/web/src/utils/utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/judicial-system/web/src/routes/Shared/IndictmentOverview/IndictmentOverview.tsx
🧰 Additional context used
📓 Path-based instructions (2)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/judicial-system/web/src/utils/utils.ts (1)

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
📓 Learnings (1)
apps/judicial-system/backend/src/app/modules/case/limitedAccessCase.service.ts (1)
Learnt from: gudjong
PR: island-is/island.is#16389
File: apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCaseFilesRecordPdfRolesRules.spec.ts:1-1
Timestamp: 2024-11-12T15:15:20.157Z
Learning: The usage of `defenderRule` in the following test files is intentional and should remain unchanged:

- `apps/judicial-system/backend/src/app/modules/file/test/limitedAccessFileController/createPresignedPostRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/file/test/limitedAccessFileController/deleteCaseFileRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/file/test/limitedAccessFileController/createCaseFileRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/file/test/limitedAccessFileController/getCaseFileSignedUrlRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRulingPdfRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getRequestPdfRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getCourtRecordPdfRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getByIdRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/limitedAccessCaseController/getAllFilesZipRolesRules.spec.ts`
- `apps/judicial-system/backend/src/app/modules/case/test/caseController/getAllRolesRules.spec.ts`

Comment on lines +633 to +655
private shouldDisplayGeneratedPdfFiles = (theCase: Case, user?: TUser) =>
Boolean(
isProsecutionUser(user) ||
theCase.defendants?.some(
(defendant) =>
defendant.isDefenderChoiceConfirmed &&
defendant.caseFilesSharedWithDefender &&
defendant.defenderNationalId &&
normalizeAndFormatNationalId(user?.nationalId).includes(
defendant.defenderNationalId,
),
) ||
theCase.civilClaimants?.some(
(civilClaimant) =>
civilClaimant.hasSpokesperson &&
civilClaimant.isSpokespersonConfirmed &&
civilClaimant.caseFilesSharedWithSpokesperson &&
civilClaimant.spokespersonNationalId &&
normalizeAndFormatNationalId(user?.nationalId).includes(
civilClaimant.spokespersonNationalId,
),
),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove duplicated code by importing from utils.ts.

This function is identical to the one in utils.ts. To avoid code duplication and maintain consistency, consider importing it from utils.ts.

+import { shouldDisplayGeneratedPdfFiles as utilsDisplayPdfFiles } from '@island.is/judicial-system/utils'

 export class LimitedAccessCaseService {
   // ...
-  private shouldDisplayGeneratedPdfFiles = (theCase: Case, user?: TUser) =>
-    Boolean(
-      isProsecutionUser(user) ||
-        theCase.defendants?.some(
-          (defendant) =>
-            defendant.isDefenderChoiceConfirmed &&
-            defendant.caseFilesSharedWithDefender &&
-            defendant.defenderNationalId &&
-            normalizeAndFormatNationalId(user?.nationalId).includes(
-              defendant.defenderNationalId,
-            ),
-        ) ||
-        theCase.civilClaimants?.some(
-          (civilClaimant) =>
-            civilClaimant.hasSpokesperson &&
-            civilClaimant.isSpokespersonConfirmed &&
-            civilClaimant.caseFilesSharedWithSpokesperson &&
-            civilClaimant.spokespersonNationalId &&
-            normalizeAndFormatNationalId(user?.nationalId).includes(
-              civilClaimant.spokespersonNationalId,
-            ),
-        ),
-    )
+  private shouldDisplayGeneratedPdfFiles = utilsDisplayPdfFiles

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +621 to +631
private getFileCategories = (theCase: Case) => {
if (isRequestCase(theCase.type)) {
return defenderCaseFileCategoriesForRequestCases
}

if (theCase.defendants?.[0].caseFilesSharedWithDefender) {
return defenderCaseFileCategoriesForIndictmentCases
}

return defenderDefaultCaseFileCategoriesForIndictmentCases
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider all defendants when determining file categories.

The function only checks defendants[0].caseFilesSharedWithDefender, which might not be accurate when there are multiple defendants with different sharing statuses.

 private getFileCategories = (theCase: Case) => {
   if (isRequestCase(theCase.type)) {
     return defenderCaseFileCategoriesForRequestCases
   }

-  if (theCase.defendants?.[0].caseFilesSharedWithDefender) {
+  if (theCase.defendants?.some(defendant => defendant.caseFilesSharedWithDefender)) {
     return defenderCaseFileCategoriesForIndictmentCases
   }

   return defenderDefaultCaseFileCategoriesForIndictmentCases
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private getFileCategories = (theCase: Case) => {
if (isRequestCase(theCase.type)) {
return defenderCaseFileCategoriesForRequestCases
}
if (theCase.defendants?.[0].caseFilesSharedWithDefender) {
return defenderCaseFileCategoriesForIndictmentCases
}
return defenderDefaultCaseFileCategoriesForIndictmentCases
}
private getFileCategories = (theCase: Case) => {
if (isRequestCase(theCase.type)) {
return defenderCaseFileCategoriesForRequestCases
}
if (theCase.defendants?.some(defendant => defendant.caseFilesSharedWithDefender)) {
return defenderCaseFileCategoriesForIndictmentCases
}
return defenderDefaultCaseFileCategoriesForIndictmentCases
}

Comment on lines +531 to +617
.then((content) => filesToZip.push({ data: content, name: file.name }))
.catch((reason) =>
// Tolerate failure, but log what happened
this.logger.warn(
`Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
{ reason },
),
)
}

filesToZip.push(
{
data: await this.pdfService.getRequestPdf(theCase),
name: 'krafa.pdf',
},
{
data: await this.pdfService.getCourtRecordPdf(theCase, user),
name: 'þingbok.pdf',
},
{
data: await this.pdfService.getRulingPdf(theCase),
name: 'urskurður.pdf',
const caseFilesByCategoryPromises = caseFilesByCategory.map(
async (file) => {
return this.awsS3Service
.getObject(theCase.type, file.key)
.then((data) => filesToZip.push({ data, name: file.name }))
.catch((reason) =>
// Tolerate failure, but log what happened
this.logger.warn(
`Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
{ reason },
),
)
},
)

await Promise.all(caseFilesByCategoryPromises)

if (isRequestCase(theCase.type)) {
filesToZip.push(
{
data: await this.pdfService.getRequestPdf(theCase),
name: 'Krafa.pdf',
},
{
data: await this.pdfService.getCourtRecordPdf(theCase, user),
name: 'Þingbók.pdf',
},
{
data: await this.pdfService.getRulingPdf(theCase),
name: 'Úrskurður.pdf',
},
)
}

if (
isIndictmentCase(theCase.type) &&
this.shouldDisplayGeneratedPdfFiles(theCase, user)
) {
filesToZip.push({
data: await this.pdfService.getIndictmentPdf(theCase),
name: 'Ákæra.pdf',
})

const caseFilesRecordPromises = theCase.policeCaseNumbers.map(
async (policeCaseNumber) => {
return this.pdfService
.getCaseFilesRecordPdf(theCase, policeCaseNumber)
.then((data) =>
filesToZip.push({
data,
name: `Skjalaskrá-${policeCaseNumber}.pdf`,
}),
)
},
)

await Promise.all(caseFilesRecordPromises)

const subpoenaPromises: Promise<number>[] = []

theCase.defendants?.map((defendant) =>
defendant.subpoenas?.map((subpoena) =>
subpoenaPromises.push(
this.pdfService
.getSubpoenaPdf(theCase, defendant, subpoena)
.then((data) =>
filesToZip.push({
data,
name: `Fyrirkall-${defendant.name}.pdf`,
}),
),
),
),
)

await Promise.all(subpoenaPromises)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling and promise chain management.

The function has several areas where error handling could be enhanced:

  1. The catch block for S3 files only logs warnings
  2. No error handling for PDF generation
  3. Nested promises in subpoena handling could be simplified
 async getAllFilesZip(theCase: Case, user: TUser): Promise<Buffer> {
   const filesToZip: { data: Buffer; name: string }[] = []
   const caseFileCategories = this.getFileCategories(theCase)
   const caseFilesByCategory =
     theCase.caseFiles?.filter(
       (file) =>
         file.key &&
         file.category &&
         caseFileCategories.includes(file.category),
     ) ?? []

   try {
     const caseFilesByCategoryPromises = caseFilesByCategory.map(
       async (file) => {
         return this.awsS3Service
           .getObject(theCase.type, file.key)
           .then((data) => filesToZip.push({ data, name: file.name }))
           .catch((error) => {
-            this.logger.warn(
+            this.logger.error(
               `Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
-              { reason },
+              { error },
             )
+            throw error
           })
       },
     )

     await Promise.all(caseFilesByCategoryPromises)

     if (isRequestCase(theCase.type)) {
       const requestFiles = await Promise.all([
         this.pdfService.getRequestPdf(theCase).then(data => ({ 
           data, 
           name: 'Krafa.pdf' 
         })),
         this.pdfService.getCourtRecordPdf(theCase, user).then(data => ({ 
           data, 
           name: 'Þingbók.pdf' 
         })),
         this.pdfService.getRulingPdf(theCase).then(data => ({ 
           data, 
           name: 'Úrskurður.pdf' 
         }))
       ])
       filesToZip.push(...requestFiles)
     }

     if (
       isIndictmentCase(theCase.type) &&
       this.shouldDisplayGeneratedPdfFiles(theCase, user)
     ) {
-      filesToZip.push({
-        data: await this.pdfService.getIndictmentPdf(theCase),
-        name: 'Ákæra.pdf',
-      })
+      const [indictmentPdf] = await Promise.all([
+        this.pdfService.getIndictmentPdf(theCase).then(data => ({
+          data,
+          name: 'Ákæra.pdf'
+        }))
+      ])
+      filesToZip.push(indictmentPdf)

       const caseFilesRecordPromises = theCase.policeCaseNumbers.map(
         async (policeCaseNumber) => {
           return this.pdfService
             .getCaseFilesRecordPdf(theCase, policeCaseNumber)
             .then((data) => ({
               data,
               name: `Skjalaskrá-${policeCaseNumber}.pdf`,
             }))
         },
       )

       const caseFilesRecords = await Promise.all(caseFilesRecordPromises)
       filesToZip.push(...caseFilesRecords)

-      const subpoenaPromises: Promise<number>[] = []
-      theCase.defendants?.map((defendant) =>
-        defendant.subpoenas?.map((subpoena) =>
-          subpoenaPromises.push(
-            this.pdfService
-              .getSubpoenaPdf(theCase, defendant, subpoena)
-              .then((data) =>
-                filesToZip.push({
-                  data,
-                  name: `Fyrirkall-${defendant.name}.pdf`,
-                }),
-              ),
-          ),
-        ),
-      )
+      const subpoenaPromises = theCase.defendants?.flatMap((defendant) =>
+        defendant.subpoenas?.map((subpoena) =>
+          this.pdfService
+            .getSubpoenaPdf(theCase, defendant, subpoena)
+            .then((data) => ({
+              data,
+              name: `Fyrirkall-${defendant.name}.pdf`,
+            }))
+        ) ?? []
+      ) ?? []

-      await Promise.all(subpoenaPromises)
+      const subpoenas = await Promise.all(subpoenaPromises)
+      filesToZip.push(...subpoenas)
     }

     return this.zipFiles(filesToZip)
+  } catch (error) {
+    this.logger.error('Failed to generate ZIP file', { error })
+    throw new InternalServerErrorException('Failed to generate ZIP file')
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const caseFileCategories = this.getFileCategories(theCase)
const caseFilesByCategory =
theCase.caseFiles?.filter(
(file) =>
file.key &&
file.category &&
defenderCaseFileCategoriesForRequestCases.includes(file.category),
caseFileCategories.includes(file.category),
) ?? []
// TODO: speed this up by fetching all files in parallel
for (const file of caseFilesByCategory) {
await this.awsS3Service
.getObject(theCase.type, file.key)
.then((content) => filesToZip.push({ data: content, name: file.name }))
.catch((reason) =>
// Tolerate failure, but log what happened
this.logger.warn(
`Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
{ reason },
),
)
}
filesToZip.push(
{
data: await this.pdfService.getRequestPdf(theCase),
name: 'krafa.pdf',
},
{
data: await this.pdfService.getCourtRecordPdf(theCase, user),
name: 'þingbok.pdf',
},
{
data: await this.pdfService.getRulingPdf(theCase),
name: 'urskurður.pdf',
const caseFilesByCategoryPromises = caseFilesByCategory.map(
async (file) => {
return this.awsS3Service
.getObject(theCase.type, file.key)
.then((data) => filesToZip.push({ data, name: file.name }))
.catch((reason) =>
// Tolerate failure, but log what happened
this.logger.warn(
`Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
{ reason },
),
)
},
)
await Promise.all(caseFilesByCategoryPromises)
if (isRequestCase(theCase.type)) {
filesToZip.push(
{
data: await this.pdfService.getRequestPdf(theCase),
name: 'Krafa.pdf',
},
{
data: await this.pdfService.getCourtRecordPdf(theCase, user),
name: 'Þingbók.pdf',
},
{
data: await this.pdfService.getRulingPdf(theCase),
name: 'Úrskurður.pdf',
},
)
}
if (
isIndictmentCase(theCase.type) &&
this.shouldDisplayGeneratedPdfFiles(theCase, user)
) {
filesToZip.push({
data: await this.pdfService.getIndictmentPdf(theCase),
name: 'Ákæra.pdf',
})
const caseFilesRecordPromises = theCase.policeCaseNumbers.map(
async (policeCaseNumber) => {
return this.pdfService
.getCaseFilesRecordPdf(theCase, policeCaseNumber)
.then((data) =>
filesToZip.push({
data,
name: `Skjalaskrá-${policeCaseNumber}.pdf`,
}),
)
},
)
await Promise.all(caseFilesRecordPromises)
const subpoenaPromises: Promise<number>[] = []
theCase.defendants?.map((defendant) =>
defendant.subpoenas?.map((subpoena) =>
subpoenaPromises.push(
this.pdfService
.getSubpoenaPdf(theCase, defendant, subpoena)
.then((data) =>
filesToZip.push({
data,
name: `Fyrirkall-${defendant.name}.pdf`,
}),
),
),
),
)
await Promise.all(subpoenaPromises)
}
const caseFileCategories = this.getFileCategories(theCase)
const caseFilesByCategory =
theCase.caseFiles?.filter(
(file) =>
file.key &&
file.category &&
caseFileCategories.includes(file.category),
) ?? []
try {
const caseFilesByCategoryPromises = caseFilesByCategory.map(
async (file) => {
return this.awsS3Service
.getObject(theCase.type, file.key)
.then((data) => filesToZip.push({ data, name: file.name }))
.catch((error) => {
this.logger.error(
`Could not get file ${file.id} of case ${file.caseId} from AWS S3`,
{ error },
)
throw error
})
},
)
await Promise.all(caseFilesByCategoryPromises)
if (isRequestCase(theCase.type)) {
const requestFiles = await Promise.all([
this.pdfService.getRequestPdf(theCase).then(data => ({
data,
name: 'Krafa.pdf'
})),
this.pdfService.getCourtRecordPdf(theCase, user).then(data => ({
data,
name: 'Þingbók.pdf'
})),
this.pdfService.getRulingPdf(theCase).then(data => ({
data,
name: 'Úrskurður.pdf'
}))
])
filesToZip.push(...requestFiles)
}
if (
isIndictmentCase(theCase.type) &&
this.shouldDisplayGeneratedPdfFiles(theCase, user)
) {
const [indictmentPdf] = await Promise.all([
this.pdfService.getIndictmentPdf(theCase).then(data => ({
data,
name: 'Ákæra.pdf'
}))
])
filesToZip.push(indictmentPdf)
const caseFilesRecordPromises = theCase.policeCaseNumbers.map(
async (policeCaseNumber) => {
return this.pdfService
.getCaseFilesRecordPdf(theCase, policeCaseNumber)
.then((data) => ({
data,
name: `Skjalaskrá-${policeCaseNumber}.pdf`,
}))
},
)
const caseFilesRecords = await Promise.all(caseFilesRecordPromises)
filesToZip.push(...caseFilesRecords)
const subpoenaPromises = theCase.defendants?.flatMap((defendant) =>
defendant.subpoenas?.map((subpoena) =>
this.pdfService
.getSubpoenaPdf(theCase, defendant, subpoena)
.then((data) => ({
data,
name: `Fyrirkall-${defendant.name}.pdf`,
}))
) ?? []
) ?? []
const subpoenas = await Promise.all(subpoenaPromises)
filesToZip.push(...subpoenas)
}
return this.zipFiles(filesToZip)
} catch (error) {
this.logger.error('Failed to generate ZIP file', { error })
throw new InternalServerErrorException('Failed to generate ZIP file')
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants