Skip to content

Adding application type Quadlet support#375

Closed
liatb-rh wants to merge 1 commit into
flightctl:mainfrom
liatb-rh:main
Closed

Adding application type Quadlet support#375
liatb-rh wants to merge 1 commit into
flightctl:mainfrom
liatb-rh:main

Conversation

@liatb-rh

@liatb-rh liatb-rh commented Nov 11, 2025

Copy link
Copy Markdown

Inline applications now support Compose/Quadlet in the editor, appType is preserved from the API, and emitted on save.
Vibe coding
Screenshot 2025-11-11 at 16 49 41

Summary by CodeRabbit

  • New Features
    • Added application format selector (Compose/Quadlet) for inline applications in the device wizard.
    • Application type preferences are now preserved when switching between application types.

@coderabbitai

coderabbitai Bot commented Nov 11, 2025

Copy link
Copy Markdown

Walkthrough

Adds support for the appType field to device application specifications. The changes propagate appType through form handling, add it to the device spec types, introduce a UI selector for Compose/Quadlet format selection in inline apps, and ensure the field is preserved during API conversions.

Changes

Cohort / File(s) Summary
Type Definitions
libs/ui-components/src/types/deviceSpec.ts
Added AppType import from @flightctl/types; added optional appType field to AppBase.
Utility Functions
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
Updated toAPIApplication to include appType in API payload, defaulting to AppType.AppTypeCompose for compose apps; updated getApplicationValues to attach appType for both OCI_IMAGE and INLINE app cases.
UI Component
libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx
Replaced aggregated imports with module-specific imports; added appType initialization and preservation logic when switching spec types; added appType to Formik update payloads and dependency lists; introduced new Application format form group with Compose/Quadlet selector for inline apps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Changes propagate consistently through the data flow (type definitions → utilities → UI), requiring verification of the appType threading across all three files
  • New UI control (Application format selector) needs validation for proper Formik binding and default value handling
  • Verify API contract changes in toAPIApplication align with backend expectations

Possibly related PRs

  • EDM-1256: Inline applications #240: Modifies the same application-spec and AppForm/AppSpec type handling with related appType field propagation.
  • Quadlet spec #371: Introduces the AppType enum (including "quadlet" option) and artifact application types; directly enables the appType selector added in this PR.
  • EDM-1511: Keep app volumes #317: Modifies the same device spec conversion code (deviceSpecUtils.ts and AppBase) to carry additional app metadata.

Suggested reviewers

  • celdrake
  • kkyrazis

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding support for Quadlet as an application type option in the inline application editor, which is the primary feature introduced across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

206-242: Add appType to image-based application API payload.

The backend ApplicationProviderSpec type supports appType for all three application variants (image, artifact, and inline). However, toAPIApplication only includes appType for inline applications (line 231), omitting it for image applications (lines 220-226).

This creates data loss during round-trip editing: getApplicationValues reads appType from the API for both image and inline apps, but only inline apps preserve it when sent back via toAPIApplication.

Add appType to the image application payload at line 226:

const data = {
  image: app.image,
  envVars,
  volumes,
  name: app.name,
  appType: app.appType || AppType.AppTypeCompose,
};
return app.name ? data : { ...data, name: undefined };
🧹 Nitpick comments (2)
libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx (2)

47-57: Refactor to use AppType enum instead of type-cast string literal.

The type casting ('compose' as unknown as AppForm['appType']) is a code smell that bypasses TypeScript's type safety. Since deviceSpecUtils.ts uses AppType.AppTypeCompose as the default value (line 231), this code should follow the same pattern for consistency and type safety.

Apply this change to use the enum value:

+import { AppType } from '@flightctl/types';
+
 const ApplicationSection = ({ index, isReadOnly }: { index: number; isReadOnly?: boolean }) => {
   // ...
   React.useEffect(() => {
     if (isInlineIncomplete) {
       setValue(
         {
           specType: AppSpecType.INLINE,
-          appType: (app.appType || ('compose' as unknown as AppForm['appType'])),
+          appType: app.appType || AppType.AppTypeCompose,
           name: app.name || '',
           files: [{ path: '', content: '' }],
           variables: [],
         },

100-112: Consider using AppType enum values for improved maintainability.

The FormSelect uses hardcoded string literals 'compose' and 'quadlet' as keys (lines 104-105). While this may work if the enum values match these strings exactly, using the enum values directly would provide better type safety and maintainability, preventing potential mismatches during refactoring.

Consider this approach using enum values:

+import { AppType } from '@flightctl/types';
+
 // ...
         {isInlineAppForm(app) && (
           <FormGroup label={t('Application format')} isRequired>
             <FormSelect
               items={{
-                compose: t('Compose'),
-                quadlet: t('Quadlet'),
+                [AppType.AppTypeCompose]: t('Compose'),
+                [AppType.AppTypeQuadlet]: t('Quadlet'),
               }}
               name={`${appFieldName}.appType`}
               placeholderText={t('Select a format')}
               isDisabled={isReadOnly}
             />
           </FormGroup>
         )}

This ensures compile-time checking and makes any enum value changes immediately visible.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between edd2687 and 7236599.

📒 Files selected for processing (3)
  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (3 hunks)
  • libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx (4 hunks)
  • libs/ui-components/src/types/deviceSpec.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 371
File: libs/types/models/AppType.ts:10-10
Timestamp: 2025-10-29T16:47:29.614Z
Learning: PR #371 (flightctl/flightctl-ui) adds the AppTypeQuadlet enum member and related types as a preparatory change. Full implementation of quadlet application support in the UI will be added later, after backend support is available.
📚 Learning: 2025-10-29T16:47:29.614Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 371
File: libs/types/models/AppType.ts:10-10
Timestamp: 2025-10-29T16:47:29.614Z
Learning: PR #371 (flightctl/flightctl-ui) adds the AppTypeQuadlet enum member and related types as a preparatory change. Full implementation of quadlet application support in the UI will be added later, after backend support is available.

Applied to files:

  • libs/ui-components/src/types/deviceSpec.ts
📚 Learning: 2025-10-10T11:35:04.458Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 0
File: :0-0
Timestamp: 2025-10-10T11:35:04.458Z
Learning: In the flightctl-ui repository, TypeScript types are generated using hey-api/openapi-ts and output to a single index.ts file (previously used openapi-typescript-codegen with one file per type under /libs/types/models).

Applied to files:

  • libs/ui-components/src/types/deviceSpec.ts
📚 Learning: 2025-03-20T12:37:36.986Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 240
File: libs/ui-components/src/types/deviceSpec.ts:0-0
Timestamp: 2025-03-20T12:37:36.986Z
Learning: In the FlightCtl UI project, `name` is required for inline applications but optional for image applications. This is enforced at the type level in the `InlineAppForm` type.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx
  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
📚 Learning: 2025-02-17T08:52:50.747Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 218
File: libs/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx:109-111
Timestamp: 2025-02-17T08:52:50.747Z
Learning: When wrapping a Button component (like `deleteAction`) inside another Button in React/PatternFly, use `component="a"` on the outer Button to avoid accessibility issues caused by button nesting. Include `tabIndex={0}` to ensure proper keyboard navigation.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx
📚 Learning: 2025-03-19T08:55:03.335Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 240
File: libs/ui-components/src/components/Device/DeviceDetails/DeviceApplications.tsx:44-50
Timestamp: 2025-03-19T08:55:03.335Z
Learning: In FlightCtl, the name field is required for Inline applications but optional for Image-based applications. This requirement is enforced by validation logic even though TypeScript type definitions might not explicitly show this distinction.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx
📚 Learning: 2025-03-19T08:57:16.233Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 240
File: libs/ui-components/src/components/Device/DeviceDetails/DeviceApplications.tsx:44-50
Timestamp: 2025-03-19T08:57:16.233Z
Learning: In FlightCtl, the name field is required for Inline applications but optional for Image-based applications. This requirement is enforced by both frontend validation logic and backend API constraints, ensuring that no inline application can exist in the system without a name field.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx
📚 Learning: 2025-03-12T09:09:44.139Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 227
File: libs/ui-components/src/components/Device/EditDeviceWizard/EditDeviceWizard.tsx:87-87
Timestamp: 2025-03-12T09:09:44.139Z
Learning: The `getUpdatePolicyValues` function in libs/ui-components/src/components/Fleet/CreateFleet/fleetSpecUtils.ts is designed to safely handle undefined values for the updatePolicy parameter.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
🧬 Code graph analysis (2)
libs/ui-components/src/types/deviceSpec.ts (1)
libs/types/index.ts (1)
  • AppType (15-15)
libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx (1)
libs/ui-components/src/types/deviceSpec.ts (2)
  • AppForm (86-86)
  • isInlineAppForm (96-96)
🔇 Additional comments (6)
libs/ui-components/src/types/deviceSpec.ts (2)

2-2: LGTM - Clean import of AppType.

The import follows the established pattern for generated types from the @flightctl/types package.


48-58: LGTM - AppType field properly integrated.

The optional appType field is correctly added to AppBase, making it available to both ImageAppForm and InlineAppForm. The optional nature ensures backward compatibility.

libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

391-420: LGTM - AppType correctly propagated from API to form values.

The getApplicationValues function correctly propagates appType from the API specifications to form values for both image-based (line 399) and inline (line 409) applications. This ensures the field is preserved when loading existing device specs for editing.

libs/ui-components/src/components/Device/EditDeviceWizard/steps/ApplicationTemplates.tsx (3)

3-6: LGTM - Import optimization for tree-shaking.

The change from aggregated imports to direct, module-specific imports from PatternFly components can improve tree-shaking and reduce bundle size.


58-70: LGTM - Proper state management and dependency tracking.

The code correctly preserves appType when switching application types (line 62) and properly includes it in the useEffect dependency array (line 70), ensuring the effect runs when appType changes and preventing stale closures.


100-112: Well-implemented conditional UI feature.

The application format selector is appropriately shown only for inline applications where the compose/quadlet distinction is relevant. The integration with the existing form structure and Formik is clean and follows established patterns.

@celdrake celdrake 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.

We'd be missing validations since for quadlet applications we only want to allow the user to set certain types (eg. .container, .volume), while attempting to save other types (eg. .kube) would get a 400 on the API side.

setValue(
{
specType: AppSpecType.INLINE,
appType: (app.appType || ('compose' as unknown as AppForm['appType'])),

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.

Seems like it would work without the casting

Suggested change
appType: (app.appType || ('compose' as unknown as AppForm['appType'])),
appType: app.appType,

@celdrake

Copy link
Copy Markdown
Collaborator

Full implementation here #381

@celdrake celdrake closed this Nov 24, 2025
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