Skip to content

[Issue #11350] Single attachment application upload - virus scanning - #11648

Open
freddieyebra wants to merge 39 commits into
mainfrom
fyebra/11350-single-attachment-virus-scan
Open

[Issue #11350] Single attachment application upload - virus scanning#11648
freddieyebra wants to merge 39 commits into
mainfrom
fyebra/11350-single-attachment-virus-scan

Conversation

@freddieyebra

@freddieyebra freddieyebra commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Work for #11350

This is blocked by removing the duplicate create attachment PR

Changes proposed

This pr integrates the virus scanning component into the single attachment form fields.

  • New application single attachment widget
  • New api route to invoke the new create application attachment endpoint
  • Temporary conditional logic to render the new widget based on form type
  • Only persist application attachment deletes and creation on form save
  • Audit logging for application form deletes and creation only on form save
  • Use axios for uploading files to S3 presigned uploads. This was required due to nextjs stripping the Content-Length header on the native fetch requests

Context for reviewers

The new component integrated in this pr will only be supported in the Attachment form for initial integration and eventually for all single attachment uploads. This integration also ensure that application attachments and attachment audit events are only persisted once the form is saved.

Validation steps

  • Start the backend and run make db-seed-local && make run-logs
  • Run the frontend npm run local or npm run dev
  • Start an application that contains the "Attachment Form"

Creating attachment

  • Add an attachment to the form and click save
  • Verify the attachment was persisted into the db by refreshing page
  • Navigate back to the application page and verify an entry for the new attachment is in application history table
  • Navigate back to the attachment form and add another attachment
  • Refresh or navigate away
  • Verify that the attachment does not exist on the form
  • Click save form and re-verify that the attachment still no longer exists
  • Navigate back to the application and verify that no entry exists in the application audit table

Deleting attachments

  • Create multiple attachments on the attachment form if none does not exist and save the form
  • Delete an attachment and verify confirmation modal appears
  • Confirm delete and save the form
  • Verify the attachment is not persisted onto the form
  • Navigate back to the application page and verify associated delete entry in the application audit table
  • Navigate back to the attachment form and delete an attachment
  • Refresh or navigate away
  • Verify that the attachment still exists on the form
  • Click save and verify the attachment still exists
  • Navigate back to the application and verify that no entry of deletion on the application audit table exists

Submission

  • Complete an application with the Application Form
  • Add 1 or more attachments to the form and save the form
  • Navigate to the application page
  • Ensure "Include form is submission" column is set to True
  • Submit the Application
  • In /api run make cmd args="task create-application-submission"
  • Refresh page and download submission
  • Ensure the expected attachment exist in the zip
  • Navigate to /print/application/<application_uuid>/form/<form_uuid> and verify the print view correctly renders the attachments

Test coverage
Screenshot 2026-07-31 at 6 08 53 PM

@ErinPattisonNava ErinPattisonNava left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did a quick pre-review, looks like the right patterns. One request is to post the test coverage when you create a pr since this is such a key component for forms.

applicationStatus,
createdAt,
updatedAt,
useVirusScanning = false,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'd add a comment here that this should be deleted in #11352

requesterForEndpoint(fetchCompetitionEndpoint),
);

export const fetchApplicationWithMethod = (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should probably have a line in the same ticket mentioned above to remove this code too.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This will still have alpha endpoints that use but the attachment api endpoints themselves will be removed in the following ticket.

import { Attachment } from "src/types/attachmentTypes";
import { UploadFileMetadata } from "src/types/fileUploadTypes";

export const ApplicationAttachmentStatus = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can these not be pulled out in to the translation file?

@freddieyebra
freddieyebra marked this pull request as ready for review July 31, 2026 19:58

for (const { testName, orgLabel } of applicantScenarios) {
test(
test.skip(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This will be re-enabled in #11353

{error && (
<FieldErrors fieldName={id} rawErrors={rawErrors as string[]} />
)}
<SimplerFileInput

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

disabled and readOnly are applied to the native file input, but they are not passed to FileInputExistingFiles. That means an existing attachment’s Delete button can remain active on a locked or read-only form.

Image

This may be something we mark as a platform update - fix for platform - or, address later

);
const { applicationId } = useParams<{ applicationId: string }>();
const { attachments } = useApplicationAttachments();
const [attachment, setAttachment] = useState<Attachment | null>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The attachment is initialized from value and the attachment context only once.

If attachments load later, Saving returns refreshed data, or the parent changes/reset the value while the widget remains mounted, the hidden value and displayed file can be stale.

We could synchronize the local state when value or attachments changes.

const describedby = error
? `error-for-${visibleInputId}`
: title
? `label-for-${visibleInputId}`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the required-field error relationship my currently be broken.

FieldErrors receives fieldName={id}, which renders an ID like error-for-att1, but the visible input is passed error-for-att1-visible.

{error && (
<FieldErrors fieldName={id} rawErrors={rawErrors as string[]} />
)}
<SimplerFileInput

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The story requires the form to be marked dirty when an upload starts. SimplerFileInput supports onStart, and ApplyForm exposes formContext.widgetSupport.markFormDirty, but this widget does not connect them.

Deleting an attachment also clears the local form value without explicitly marking the form dirty.
Could we wire markFormDirty to upload start and deletion?

myduong-navapbc
myduong-navapbc previously approved these changes Aug 4, 2026
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.

4 participants