Skip to content

feat(frontend): add Python UDF UI parameter form support#5043

Open
carloea2 wants to merge 19 commits into
apache:mainfrom
carloea2:ui-parameter-frontend
Open

feat(frontend): add Python UDF UI parameter form support#5043
carloea2 wants to merge 19 commits into
apache:mainfrom
carloea2:ui-parameter-frontend

Conversation

@carloea2
Copy link
Copy Markdown
Contributor

@carloea2 carloea2 commented May 13, 2026

What changes were proposed in this PR?

This PR adds frontend building blocks for Python UDF UI parameters.

It introduces:

Area Change
Python parameter parser Adds a parser service for detecting supported self.UiParameter(...) declarations.
Parameter sync utility Adds a service that can merge inferred parameter structure with existing edited values and report invalid UI parameter declarations.
Formly field type Adds and registers a ui-udf-parameters Formly component for displaying parameter rows.
Shared parser/sync type Defines the frontend UiUdfParameter shape in the parser service and imports it from the sync service.
Dependency Adds @lezer/python@1.1.18 for Python syntax parsing. This dependency is MIT licensed.

This PR does not complete the end-to-end feature by itself. The parser/sync utilities and Formly field type are added here, but full wiring with backend operator descriptors and runtime injection is handled by later PRs in the stack.

Any related issues, documentation, discussions?

Part of the Python UDF UI parameter feature split from feat/ui-parameter.

#5044

Intended stack order:

  1. Frontend UI parameter building blocks
  2. Scala backend injection model
  3. Python runtime support
  4. End-to-end integration

This PR introduces:

sequenceDiagram
    autonumber

    participant Code as Python UDF Code
    participant Parser as UiUdfParametersParserService
    participant Sync as UiUdfParametersSyncService
    participant Type as UiUdfParameter type
    participant Component as UiUdfParametersComponent
    participant Formly as Formly Config
    participant Later as Later PRs

    Code->>Parser: self.UiParameter(...) declarations
    Parser-->>Type: name/type/value metadata
    Parser-->>Sync: inferred parameter structure
    Sync->>Sync: preserve existing edited values

    Component->>Formly: register ui-udf-parameters field type

    Type-->>Later: available for descriptor wiring
    Formly-->>Later: field can be used when schema exposes it

    Note over Parser,Formly: This PR adds frontend building blocks only.
    Note over Later: Rendering in the operator panel depends on later backend/schema wiring.
Loading

How was this PR tested?

Added frontend unit tests for the parser, sync service, and Formly component.

Commands run:

cd frontend
node .yarn/releases/yarn-4.14.1.cjs format:ci
node .yarn/releases/yarn-4.14.1.cjs build:ci
node .yarn/releases/yarn-4.14.1.cjs test --watch=false --include=src/app/workspace/service/code-editor/ui-udf-parameters-parser.service.spec.ts --include=src/app/workspace/service/code-editor/ui-udf-parameters-sync.service.spec.ts --include=src/app/workspace/component/ui-udf-parameters/ui-udf-parameters.component.spec.ts

Manual UI testing is not available in this PR alone because the backend/operator schema wiring that makes the field visible in the operator panel is intentionally handled in later PRs.

Was this PR authored or co-authored using generative AI tooling?

No

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file frontend Changes related to the frontend GUI labels May 13, 2026
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 13, 2026

Codecov Report

❌ Patch coverage is 78.26087% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.32%. Comparing base (538ae1d) to head (2222509).

Files with missing lines Patch % Lines
...ui-udf-parameters/ui-udf-parameters.component.html 0.00% 17 Missing ⚠️
...ce/code-editor/ui-udf-parameters-parser.service.ts 87.87% 2 Missing and 10 partials ⚠️
...vice/code-editor/ui-udf-parameters-sync.service.ts 86.95% 2 Missing and 4 partials ⚠️
...t/ui-udf-parameters/ui-udf-parameters.component.ts 76.19% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5043      +/-   ##
============================================
+ Coverage     43.16%   43.32%   +0.15%     
  Complexity     2212     2212              
============================================
  Files          1045     1049       +4     
  Lines         40363    40547     +184     
  Branches       4268     4318      +50     
============================================
+ Hits          17423    17567     +144     
- Misses        21866    21890      +24     
- Partials       1074     1090      +16     
Flag Coverage Δ *Carryforward flag
access-control-service 39.53% <ø> (ø) Carriedforward from 5afd325
agent-service 33.72% <ø> (ø) Carriedforward from 5afd325
amber 43.80% <ø> (ø) Carriedforward from 5afd325
computing-unit-managing-service 0.00% <ø> (ø) Carriedforward from 5afd325
config-service 0.00% <ø> (ø) Carriedforward from 5afd325
file-service 32.18% <ø> (ø) Carriedforward from 5afd325
frontend 34.56% <78.26%> (+0.49%) ⬆️
python 90.50% <ø> (ø) Carriedforward from 5afd325
workflow-compiling-service 56.81% <ø> (ø) Carriedforward from 5afd325

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

@chenlica chenlica requested a review from Xiao-zhen-Liu May 13, 2026 06:07
@Xiao-zhen-Liu
Copy link
Copy Markdown
Contributor

Xiao-zhen-Liu commented May 13, 2026

@carloea2 How do I test the features of this PR? In general, just relying on unit tests is not enough, and we should explicitly explain how a new feature can be tested manually.

@carloea2
Copy link
Copy Markdown
Contributor Author

Hi @Xiao-zhen-Liu, this PR cannot be manually tested yet because it only adds the components that will be used later to update the UI, specifically the Parser and Sync Services.

I intentionally kept this PR isolated to reduce the number of lines changed. The Formly components are tightly coupled to the operator schemas, so introducing them will require backend changes and user-facing updates that will only work once all four PRs are merged.

Copy link
Copy Markdown
Contributor

@Xiao-zhen-Liu Xiao-zhen-Liu left a comment

Choose a reason for hiding this comment

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

Thanks for splitting this off cleanly, @carloea2. The parser tests are well written and the overall scope is small enough to review in one sitting. Inline comments are left on specific lines. Most of them are foundation issues that will pay off when PRs #2 to #4 are built on top, so it is worth addressing them here rather than later. A few notes that do not belong on a specific line:

PR description: The "Type definitions" row in the table ("Extends frontend workflow-compiling types to represent UI parameter metadata") is confusing. UiUdfParameter is actually defined inline in the parser service file, and workflow-compiling.interface.ts is not modified. This row would be clearer if either the interface gets moved into workflow-compiling.interface.ts (since it is shared between the two services), or the row is rephrased to describe what was actually added.

Test gaps to fill:

  • Parser: subclass case (see comment on CLASSES), multiple classes in one file, empty arg list, extra positional arg, duplicate parameter names (currently silently deduped).
  • Sync service: attachToYCode happy path (subscribe, mutate YText, observe emission, call cleanup), the operator-type filter, the path where codeFromEditor is omitted.
  • Component: there is no spec at all. A minimal test asserting that name and type fields are disabled and the value field is editable would catch the next refactor accident.

On manual testing: Since this PR does not affect any user-facing behavior, shipping it without a manual testing flow is fine. For future PRs in this stack, please think about including a small dev-only setup (a hidden route, an internal test page, a minimal example schema) so reviewers can exercise the new piece in isolation. It is much easier to give good feedback when the new component can be seen rendering.

Comment thread frontend/package.json
Comment thread frontend/src/app/workspace/service/code-editor/ui-udf-parameters-sync.service.ts Outdated
Comment thread frontend/src/app/workspace/service/code-editor/ui-udf-parameters-sync.service.ts Outdated
@carloea2
Copy link
Copy Markdown
Contributor Author

@Xiao-zhen-Liu thanks, I will ping you when I have the fixes ready.

@carloea2
Copy link
Copy Markdown
Contributor Author

@Xiao-zhen-Liu It is ready for the next review pass. Thanks.

Copy link
Copy Markdown
Contributor

@Xiao-zhen-Liu Xiao-zhen-Liu left a comment

Choose a reason for hiding this comment

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

Thanks @carloea2, the rewrite is a real improvement. All previous comments are addressed, and the new error-surfacing design with UiUdfParametersParseError plus the expanded test coverage for both services are nice additions beyond what was asked. Three small things below that do not need to block this PR. Approving.

Comment on lines +38 to +44
ngOnInit(): void {
this.field.fieldGroup?.forEach(rowField => {
this.fieldColumns.forEach(column => {
this.configureDisabledState(this.getColumnField(rowField, column), column.disabled);
});
});
}
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.

The disable logic runs in ngOnInit, which only iterates the rows that exist at component init time. When PR #2 wires the uiParametersChanged$ subscriber and writes back into operatorProperties.uiParameters, FieldArrayType will generate new rows from this.field.fieldArray (the row template). Those new rows do not go through ngOnInit, and the onInit hooks registered inside configureDisabledState are attached to the existing field configs, not to the template. New rows would then render with Name and Type editable.

This is not visible today because no subscriber exists in this PR. But it will be a regression as soon as PR #2 lands. Two cleaner options: (1) set disabled: true directly on field.fieldArray.fieldGroup entries for Name and Type so every new row inherits the state from the template; or (2) override FieldArrayType.onPopulate so the disable logic runs whenever rows are populated.

The component spec also only constructs a static field.fieldGroup and never adds rows, so this case is not exercised. Please either pick a fix now or add a spec that adds a row after ngOnInit and asserts the new row's Name and Type are disabled, so this regression is caught when PR #2 lands.

Comment on lines +108 to +113
const operatorProperties = sharedOperatorType.get("operatorProperties") as any;
const yCode = operatorProperties.get("code") as YText;
return yCode?.toString();
} catch {
return undefined;
}
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.

Two small things in this helper. (1) The catch {} block silently returns undefined for every error type, which could hide real bugs during shared-editing debugging. A console.warn on the caught error would help. (2) sharedOperatorType.get("operatorProperties") as any was previously typed as as YType<Readonly<{ [key: string]: any }>>. Restoring that (or a tighter type) keeps the editor-to-graph boundary type-safe.

throw error;
}

this.uiParametersParseErrorSubject.next({ operatorId });
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.

This emits { operatorId, message: undefined } to clear errors, so subscribers have to check message to distinguish "no error" from "error". This works, but reads a bit awkwardly. When PR #2 consumes this, a clearParseError(operatorId) method (or a separate uiParametersParseErrorCleared$ observable) would express the intent more directly.

@Xiao-zhen-Liu
Copy link
Copy Markdown
Contributor

@carloea2 one more pass before merging: the new code (parser service, sync service, and component) has very little documentation beyond the license header. For a foundation PR that PRs #2 to #4 will build on top of, the public contracts need to be readable without diving into method bodies. Please add JSDoc on:

Parser service

  • The UiUdfParametersParserService class (one or two sentences on what it does and when to use it).
  • parse(code): what it accepts, what it returns, and the conditions under which it throws UiUdfParametersParseError.
  • The exported UiUdfParameter type and the UiUdfParametersParseError class.

Sync service

  • The UiUdfParametersSyncService class.
  • uiParametersChanged$ and uiParametersParseError$: what each one emits, when, and (for the error stream) how subscribers should interpret a { operatorId, message: undefined } event.
  • attachToYCode(operatorId, yCode): the lifecycle, the meaning of the returned cleanup function, and whether it is safe to call more than once for the same operator.
  • syncStructureFromCode(operatorId, codeFromEditor?): the precondition (operator must be a Python UDF, code must be defined) and what the consumer is expected to do when an event is emitted.
  • A short note next to UI_PARAMETER_SYNC_DEBOUNCE_TIME_MS = 200 explaining the reasoning, so the value is not changed later without that context.

Component

  • The UiUdfParametersComponent class (one sentence on what it renders).
  • getColumnField and configureDisabledState (one line each, since these are the only non-trivial surfaces).

The bodies are well-written and not hard to follow, but the next person extending this code should not have to read every function to understand what each promises. This does not need to be exhaustive: one or two sentences per public surface is enough.

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

Labels

dependencies Pull requests that update a dependency file frontend Changes related to the frontend GUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants