Skip to content

refactor(form-intakes): improve form intakes architecture (#18274) - #18314

Open
Octave (OctaveLaventure) wants to merge 38 commits into
masterfrom
issue/18274-form-intakes-architecture-refactor
Open

Octave (OctaveLaventure) wants to merge 38 commits into
masterfrom
issue/18274-form-intakes-architecture-refactor

Conversation

@OctaveLaventure

Copy link
Copy Markdown
Contributor

Description

Deepens the Form Intake feature's architecture (both frontend form editing and backend form ingestion, draft and non-draft modes) per the 5 candidates identified in an /improve-codebase-architecture review. Pure refactor, no observable behavior change.

Closes #18274

Backend

  • buildDraftPlan: centralizes the draft-mode choreography previously inlined in formSubmit's draft branch (form-domain.ts).
  • planSubmission / commitSubmission: splits formSubmit into a pure decision step (planSubmission: builds the bundle/draft plan, no I/O) and an execution step (commitSubmission: work creation, queue push, patch, telemetry). formSubmit is now a thin 2-call composition.
  • EntitySourceAdapter map: extracts buildMainStixEntities's 4 source-mode branches (lookup, multiple, parsed, default) into a Record<Mode, EntitySourceAdapter> map + a mode resolver, preserving every per-branch buildMaterializeOptions asymmetry exactly.

Frontend

  • computeDraftPolicy: centralizes draft field visibility/required/validation rules that were previously duplicated 3x in FormView.tsx (init values, Yup validation, render conditions) into a single computed DraftPolicy.
  • useFormBuilderOperations: introduces named FormBuilderData operations (renameField, toggleParsedMode, addAdditionalEntity, updateRelationshipEntity, updateRelationshipType, toggleRelationshipRequired) replacing the primary uses of the generic handleFieldChange(path, value) string-path walker. Genuinely dynamic/generic settings paths remain on handleFieldChange by design.

How to test this PR

Backend:

cd opencti-platform/opencti-graphql
yarn test:ci-unit
yarn check-ts

Frontend:

cd opencti-platform/opencti-front
yarn vitest run src/private/components/data/forms
yarn check-ts

Manually, verify form creation/edition and form submission (draft and non-draft) still behave identically:

  • Create/edit a form schema in the Form Intake builder (rename a field, toggle parsed mode, add an additional entity, edit a relationship).
  • Submit a public form both as a draft and as a direct (non-draft) submission, for each main-entity source mode (lookup, multiple observables, parsed/pattern, default single entity).

Notes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lizer

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Task B2's type-check surfaced 5 pre-existing TS2345 errors in Task B1's
new validateFormSchemaMappings test fixtures: unannotated object-literal
spreads let mainEntityFieldMode widen to string instead of narrowing to
the 'multiple' | 'parsed' union, so passing them to validateFormSchemaMappings
(FormBuilderData) failed to type-check. Runtime behavior was unaffected
(tests passed at runtime), but yarn check-ts was broken. Annotate each
fixture as FormBuilderData to restore a clean type-check.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…actor

- Remove unused normalizeDraftAuthorizedMembersDefaults import from
  FormSchemaEditor.tsx, left behind after Task D2 moved its only usage
  into DraftDefaultsSection.tsx (fixes a yarn lint CI failure)
- Harden fieldRendererRegistry with Object.create(null) so unknown
  field.type values (e.g. 'constructor', 'toString') cannot resolve to
  inherited Object.prototype members instead of falling back to the
  registry's 'default' renderer, which previously crashed React
- Apply yarn lint --fix formatting corrections triggered by the above
  changes (import-newlines, indentation) with no behavior changes
- Add a regression test asserting the registry does not leak
  Object.prototype members for unknown field types
…ndle-builder call sites

Addresses a Data Clump smell flagged in code review: all 6 call sites of
materializeEntityFromFields in form-bundle-builder.ts built a near-identical
MaterializeOptions object inline. Adds buildMaterializeOptions(isBypass, overrides)
in form-entity-materializer.ts, centralizing the 3 defaults shared by every branch
(applyFields: true, skipEmptyFieldValues: false, applyTypeDefaults: true) while
requiring each call site to still pass errorLabel and any per-branch override
explicitly, preserving the deliberate per-branch skip-empty/type-default asymmetry
rather than collapsing it into a generic enum/mode that would misleadingly imply
the branches are equivalent.
- form-entity-materializer-test.ts: cast through unknown to access the
  dynamically-applied 'details' field, which is not a declared StoreEntity
  property (tsc error TS2339).
- FormSchemaEditor.tsx: collapse the FormUtils import back to a single line
  (import-newlines/enforce requires single-line imports for <=20 elements).
…ion gate failures

- Mark Switch/TextField imports in DraftDefaultsSection.tsx and
  MainEntitySection.tsx as fds:keep-mui, consistent with sibling
  extracted section components (these imports predate this PR).
- Migrate fileFieldRenderer.tsx's file-chip from MUI Chip to the
  design system Chip (onDelete is supported natively).
- Update fds-migration/migration-state.json's Checkbox adoption-site
  entry to point at primitiveFieldRenderers.tsx, where the checkbox
  rendering now lives after the field-renderer registry extraction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ew findings (#18274)

- planSubmission: wrap buildDraftPlan in the same try/catch shape
  commitSubmission uses, so a failure while building the draft plan is
  logged and surfaced as a FunctionalError like it was before the
  planSubmission/commitSubmission split, instead of propagating raw.
- fileFieldRenderer.tsx: clarify the 'Stays MUI' comment so it clearly
  scopes to the native file input widget only, not the file-list Chip
  below (which already uses the design-system Chip).
- useFormBuilderOperations: document that toggleParsedMode assumes a
  strictly binary fieldMode ('multiple' | 'parsed').
- FormViewUtils computeDraftPolicy: remove the dead
  'author.type === static && defaultValue' clause from author.initialized
  -- FormView.tsx already special-cases that combination in its own
  branch before checking initialized, so the clause never affected
  behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 16:20
@OctaveLaventure Octave (OctaveLaventure) added the filigran team Item from the Filigran team. label Sep 16, 2026
@OctaveLaventure Octave (OctaveLaventure) added the form intake Feature or bug related to "Form Intake" capability label Sep 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Refactors Form Intake architecture across backend submission/materialization and frontend form editing/rendering while preserving behavior.

Changes:

  • Splits submission planning from side effects and extracts draft planning.
  • Replaces entity-source branching with adapters and centralizes materialization.
  • Extracts frontend policies, operations, renderers, and adds tests.

Reviewed changes

Copilot reviewed 43 out of 44 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
opencti-platform/opencti-graphql/tests/01-unit/modules/forms/form-entity-materializer-test.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/tests/01-unit/modules/forms/form-bundle-builder-test.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/tests/01-unit/domain/form-plan-submission-test.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/tests/01-unit/domain/form-draft-plan-test.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/tests/01-unit/domain/form-commit-submission-test.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/src/modules/form/form-entity-materializer.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/src/modules/form/form-domain.ts Updated as part of this pull request.
opencti-platform/opencti-graphql/src/modules/form/form-bundle-builder.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/FormViewUtils.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/FormViewUtils.test.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/FormView.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/FormFieldRenderer.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/types.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/types.test.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/registry.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/registry.test.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/primitiveFieldRenderers.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/primitiveFieldRenderers.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/fileFieldRenderer.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/fileFieldRenderer.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/dateAndReferenceFieldRenderers.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/dateAndReferenceFieldRenderers.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/choiceFieldRenderers.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/choiceFieldRenderers.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/useFormSchemaEditorStyles.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/useFormBuilderOperations.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/useFormBuilderOperations.test.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/useFieldRenderer.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/RelationshipsSection.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/RelationshipsSection.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/MainEntitySection.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/MainEntitySection.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/FormUtils.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/FormUtils.test.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/FormEdition.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/FormCreation.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/Form.d.ts Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/DraftDefaultsSection.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/DraftDefaultsSection.test.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/AdditionalEntitiesSection.tsx Updated as part of this pull request.
opencti-platform/opencti-front/src/private/components/data/forms/AdditionalEntitiesSection.test.tsx Updated as part of this pull request.
fds-migration/migration-state.json Updated as part of this pull request.
Suppressed comments (3)

opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/dateAndReferenceFieldRenderers.tsx:128

  • The external-reference renderer has the same dotted-name lookup problem: for parsed/multiple overrides fieldValue becomes '', so creating an external reference does not preserve the current nested list and may pass a non-array value to append. Read the value through getNestedValue(values, fieldName) with the simple-key fallback.
  const fieldValue = fieldPrefix ? getNestedValue(values, fieldName) : (values[field.name] || '');

opencti-platform/opencti-front/src/private/components/data/forms/view/fieldRenderers/fileFieldRenderer.tsx:115

  • The new removable chip relies on the design system's default delete label, which is an untranslated Remove ${label}. In non-English locales, screen-reader users will hear English for this control; the existing Tag wrapper documents that callers must provide a translated deleteLabel. Pass a translated delete label here as well.
            <Chip
              key={index}
              label={file.name ?? ''}
              onDelete={() => handleFileRemove(index)}

opencti-platform/opencti-graphql/src/modules/form/form-domain.ts:521

  • The new planning boundary is not actually pure or I/O-free as described: these calls load entities, can invoke the pattern-conversion service, build relationships, and line 521 logs the generated bundle. Every planSubmission call still depends on external state and performs side effects before commitSubmission; either move those effects behind the commit-time plan or narrow the documented contract to no submission writes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread opencti-platform/opencti-graphql/src/modules/form/form-domain.ts
…#18274)

Fixes bugs and cleans up duplication flagged by automated and human
review on the form intake architecture refactor:

- Always resolve dotted-path field names via getNestedValue instead of
  falling back to a literal values[field.name] lookup, which broke
  whenever the field name itself contained a dot (object label,
  external references, file attachment fields).
- Fix fileFieldRenderer using the unprefixed field name when calling
  setFieldValue on upload/remove, and add a translated delete label to
  the attachment Chip for accessibility.
- Normalize the two conflicting setFieldValue contracts (relative vs.
  fully-qualified path) behind a single scopedSetFieldValue wrapper in
  FormFieldRenderer, fixing a double-prefixing bug affecting
  CreatedBy/ObjectMarking/ObjectLabel/ExternalReferences fields on
  additional entities and relationships.
- Change toggleParsedMode to take an explicit target mode instead of
  flipping the current one, fixing a bug where an omitted (legacy)
  fieldMode always flipped to parsed regardless of the option
  actually selected in the UI.
- Extract removeFieldsSupersededByParsedMapping into FormUtils and use
  it from both MainEntitySection and AdditionalEntitiesSection,
  removing duplicated field-removal logic.
- Extract a shared SPECIAL_ATTRIBUTE_FIELD_TYPE map in
  useFieldRenderer and use it in both the available-field-types
  computation and the attribute-selection handler, removing the
  duplicated special-attribute switch.
- Make RelationshipsSection update/toggle callbacks required props
  and remove the redundant handleFieldChange-based fallback
  implementations that duplicated the same mutation logic.
- Extract a shared FieldRendererInput base interface reused by
  FieldRendererContext and FormFieldRendererProps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… log side-effect (#18274)

Address review feedback that planSubmission was documented as a pure
decision step but still performs DB reads, may invoke a Python
pattern-conversion bridge, and generates a bundle id. Narrow the JSDoc
to accurately describe the function as side-effect-free for writes
(no persistence, no logging) without claiming full purity/determinism,
and move the "[FORM] STIX Bundle generated" log call out of
planSubmission into commitSubmission where the actual orchestration
and persistence happen.

Also rewrite the misleading "builds a bundle with objects" unit test,
which asserted nothing about bundle contents, to instead verify that
buildMainStixEntities/buildAdditionalEntities/buildRelationships/
wrapInContainerOrPush are invoked with the expected arguments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.86134% with 325 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.46%. Comparing base (c226684) to head (1061ae9).
⚠️ Report is 16 commits behind head on master.

Files with missing lines Patch % Lines
...ate/components/data/forms/DraftDefaultsSection.tsx 17.18% 106 Missing ⚠️
...omponents/data/forms/AdditionalEntitiesSection.tsx 39.53% 52 Missing ⚠️
...private/components/data/forms/useFieldRenderer.tsx 69.03% 48 Missing ⚠️
...ate/components/data/forms/RelationshipsSection.tsx 52.94% 40 Missing ⚠️
...rivate/components/data/forms/MainEntitySection.tsx 55.38% 29 Missing ⚠️
...ta/forms/view/fieldRenderers/fileFieldRenderer.tsx 60.86% 18 Missing ⚠️
...ms/view/fieldRenderers/primitiveFieldRenderers.tsx 60.52% 15 Missing ⚠️
...private/components/data/forms/FormSchemaEditor.tsx 0.00% 4 Missing ⚠️
.../components/data/forms/useFormBuilderOperations.ts 89.74% 4 Missing ⚠️
...src/private/components/data/forms/FormCreation.tsx 0.00% 3 Missing ⚠️
... and 2 more

❌ Your patch check has failed because the patch coverage (65.86%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #18314      +/-   ##
==========================================
+ Coverage   35.46%   37.46%   +2.00%     
==========================================
  Files        3428     3486      +58     
  Lines      139424   148068    +8644     
  Branches    37734    40624    +2890     
==========================================
+ Hits        49444    55474    +6030     
- Misses      89980    92594    +2614     
Flag Coverage Δ
opencti-client-python 49.03% <ø> (-1.17%) ⬇️
opencti-front 13.45% <60.44%> (+1.42%) ⬆️
opencti-graphql 72.15% <97.82%> (+1.49%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

filigran team Item from the Filigran team. form intake Feature or bug related to "Form Intake" capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(form-intakes): improve form intakes architecture

3 participants