Repository navigation
V2 → V3 frontend cutover (signal-native migration) - #5308
Merged
Merged
Conversation
FWT-957. invite-users-create exposes signalHandle: SignalStepHandle with submit calling a refactored runInvite() so the new signal-handle path and the legacy onNext share the same side effects. Cancel-button-text madeChanges binding preserved.
FWT-957. Migrates 5 app/service consumers to SignalStepHandle: - new-application-base-step: Shape 1 tile selector (signal+html only) - edit-application: Shape 2 single-step CRUD; form valid+dirty as signal, explicit Router.navigate to app detail - application-delete: 2 confirmation steps (Routes, Service Instances) as valid:signal(true); Confirm step Shape 2 destructive submit delegating to existing redirectToAppWall flow. Legacy startDelete arrow retained for spec compatibility. - add-route-stepper / add-routes: Shape 2; signalHandle exposed on child AddRoutesComponent. addRouteMode made signal-backed via getter/setter so step valid recomputes on radio toggle. - detach-service-instance / detach-apps: child exposes signalHandle for Unbind Apps step; parent Confirm step Shape 2 with submit- driven detachServiceBinding + Router.navigate to /services.
FWT-957. edit-profile-info exposes signalHandle: SignalStepHandle backed by toSignal of editProfileForm.statusChanges + dirty tracking, mirroring the legacy [valid]="editProfileForm.valid && dirty" binding. Submit awaits the existing save flow + Router.navigate(/user-profile).
FWT-957. 12 wizards mark inline with // FWT-957 DEFERRED: comments — these are multi-step flows with cross-step shared state that the per-step SignalStepHandle contract from FWT-956 cannot express: - Endpoint family: create-endpoint, console-uaa-wizard, git-registration (two-step Register + optional Connect with cross-component connect.doConnect/onEnter/onNext coordination) - App / service families: create-application, deploy-application, add-service-instance (cross-step ngrx state — selected service ID flows step1 -> step2 -> step3) - CF user family: manage-users (3-step + onLeave/onEnter + applyStarted toggle), remove-user (applyStarted two-click + ignoreSuccess semantic) - Autoscaler / K8s family: edit-autoscaler-policy, kube-config-registration, create-release, upgrade-release (multi-step with shared mutable state) FWT-959 introduces the cross-step coordinator primitive (SignalStepperContext<T>) and sweeps all 12, then completes the ngrx removal from SteppersComponent / StepComponent that this ticket leaves open.
The endpoint-data.service.ts load() path was calling
/pp/v1/cf/routes/{cnsi} without ?return=counts, falling through to
the full route list + ListDestinations path on the backend. That
delayed the home card's route count behind the apps fetch instead of
arriving in parallel with the orgs and apps counts.
Adding ?return=counts hits the backend per_page=1 fast path
(native_handlers.go:588) — same pattern the orgs and apps requests
already use.
Visible effect: home cards display all three counts (orgs, apps,
routes) together, then update the recently-deployed apps list when
that fetch completes.
FWT-959 Part 1. Adds 12 optional fields to SignalStepHandle so the 12 deferred Shape 3 stepper consumers (manage-users, deploy-application, edit-autoscaler-policy, kube-config-registration, etc.) can express their full @input surface as signals: blocked, hidden, onEnter, onLeave, destructiveStep, canClose, disablePrevious, finishButtonText, nextButtonText, cancelButtonText, hideCloseButton, showBusy Each existing @input on StepComponent gains a setter+getter pair — the getter prefers signalHandle.<field>?.() over legacy storage. Signal reads inside getters are tracked by Angular CD so the parent re-evaluates without explicit EventEmitter wiring. submit() resolved-value now threads { ignoreSuccess?: boolean } through StepOnNextResult — preserves the legacy applyStarted / ignoreSuccess two-click semantic that manage-users and remove-user rely on. New invokeLeave() mirrors invokeNext() for onLeave delegation; SteppersComponent.goNext() updated to call it. pOnEnter now prefers signalHandle.onEnter over legacy onEnter. Adds 17 spec cases covering the new field paths (win-vs-fall-through, state-driven toggling, onEnter/onLeave/ignoreSuccess delegation).
Follow-up to dc8a71a — the spec's ROUTES_URL constant also needs the ?return=counts query param so the HttpTestingController matches the new request shape.
Production build caught a TS2339 that vitest gate missed: the
Promise<void | { ignoreSuccess?: boolean }> resolved value can't
be safely accessed via ?.ignoreSuccess because optional chain
doesn't narrow void out of the union. Add a typeof check before
reading the property.
FWT-959 Part 2 (Partition D). Parent-owned handles for the 2-step selection→import flow. KubeConfigImportComponent.applyStarted promoted to a signal-backed getter/setter so the review step's canClose/destructiveStep/finishButtonText can be computed(). Selection step's submit is omitted (auto-advance); cross-step cluster data is read from the selector's KubeConfigHelper inside the review step's onEnter rather than the legacy onNext-data-return path. Review step's submit delegates to the importer's existing onNext to preserve the two-click "Import then Close" semantic and translates the legacy redirect: true into an explicit Router.navigate to /endpoints.
FWT-959 Part 2 (Partition D). 2-step Install Chart wizard. Details step
handle mirrors validate$ via toSignal so the form's validity gates the
Next button. Overrides step handle resizes the editor on enter and
runs the existing createNamespace + installChart pipeline on submit;
legacy { redirect: true, redirectPayload: { path } } becomes an
explicit Router.navigate to the workload summary page. Dropped the
endpointChanged toObservable indirection — read endpoint.valueChanges
directly inside the namespaces$ stream — and removed the now-unused
endpointChangedSignal field plus its feeder subscription.
FWT-959 Part 2 (Partition D). 2-step Upgrade Workload wizard. Version
step handle's valid signal is fed by the existing validate$
subscription inside the helper.hasUpgrade() callback (validate$ is
constructed lazily once the upgrade chart resolves). Overrides step
handle delegates to doUpgrade and on success navigates to cancelUrl —
the legacy { redirect: true, redirectPayload: { path: cancelUrl } }
plumbing becomes an explicit Router.navigate. showAdvancedOptions
parity branch preserved for when the advanced step is reinstated.
AsyncPipe import removed — no remaining | async in the template.
FWT-959 Part 2 (Partition A). 2-step UAA setup wizard. Step 1's handle mirrors uaaForm.valueChanges into a signal so the Next button gates on form validity, and submit() dispatches SetupConsoleGetScopes then awaits the uaaSetup store, populating uaaScopes/selectedScope on success. Step 2's submit dispatches SetupSaveConfig and awaits the combined uaa+auth state with the legacy delay/retry/VerifySession chain, hard-reloading the app on success. Cross-step state stays as plain fields read by step 2's template since step 1 populates them inside its submit before advance. Legacy uaaFormNext/uaaScopeNext StepOnNextFunctions and the validateUAAForm Observable removed in the same diff.
FWT-959 Part 2 (Partition A). 2-step inline CF/metrics fallback flow rendered when no custom registrationComponent is wired. step1's handle mirrors the step's validate Observable into a signal (subscribed in queueMicrotask because validate is constructed lazily in ngAfterContentInit), and submit delegates to the existing onNext to preserve the snackbar + dup-endpoint warning side-effects, then hands the ConnectEndpointConfig to connect.onEnter before advance — replacing the legacy onNext-data-return → pOnEnter path. Legacy redirect:true from step1 translates to Router.navigate(['/endpoints']). CreateEndpointConnectComponent grows additive validSignal / doConnectSignal alongside getter/setter shims for the legacy valid / doConnect fields, so the connect step handle's computed() reads stay reactive without polling. Public API unchanged — both create-endpoint and git-registration consumers keep working with the same template- ref bindings.
FWT-959 Part 2 (Partition A). 2-step Github/Gitlab registration wizard mirroring create-endpoint's shape. registerStepHandle mirrors the form's validate Observable into a signal subscribed inside init() (which runs after endpoints$ resolves), with an immediate initial- value set so the Next button reflects the pre-selected default type. submit calls a new runRegistration() private method (extracted from the old onNext field) and hands the ConnectEndpointConfig to connect.onEnter before advance. connectStepHandle shares the create-endpoint shape exactly, reading the connect child's signal- backed valid/doConnect via computed().
FWT-959 Part 2 (Partition C). Single Confirm step that uses the
applyStarted / ignoreSuccess two-click semantic. applyStarted
promoted to a signal-backed getter/setter so the handle's canClose /
disablePrevious / destructiveStep / finishButtonText fields can be
computed() over it. submit() returns { ignoreSuccess: true } on the
first click (apply dispatched, no auto-advance, no success snackbar)
and navigates back to defaultCancelUrl on the second click — the
legacy { redirect: true } becomes an explicit Router.navigateByUrl.
isBlocked$ bridged to a signal in ngAfterViewInit so the handle's
blocked field reacts when the store data settles. Legacy startApply
StepOnNextFunction removed in the same diff.
FWT-959 Part 2 (Partition C). 3-step Manage Roles wizard with the optional first Usernames step kept conditional via the existing @if (setUsernames) template guard. Each step's handle delegates to the corresponding child via @ViewChild — setUsernamesStepHandle and modifyStepHandle bridge the children's valid$/blocked$ observables into local signals (ngAfterViewInit subscriptions + markForCheck) so the stepper re-evaluates reactively. confirmStepHandle preserves the applyStarted / ignoreSuccess two-click apply semantic via submit() returning { ignoreSuccess: true } on the first click and explicit Router.navigateByUrl on the second — same pattern as remove-user. applyStarted promoted to a signal-backed getter/setter so the handle's canClose / disablePrevious / destructiveStep / finishButtonText fields are computed() over it. Legacy startApply StepOnNextFunction removed in the same diff.
FWT-959 Part 2 (Partition C). Final 4-step wizard in this partition. Cross-step state stays in EditAutoscalerPolicyService — children read/ write the policy via setState/getState as before. Each step's handle delegates to the corresponding child via @ViewChild — step1Handle bridges editLimitForm validity through a new valid$ observable, and step{2,3,4}Handle's valid/disablePrevious are computed() over local signals fed by each child's new editIndex$ BehaviorSubject (the private editIndex field is now a getter/setter wrapping the subject so templates and call-sites stay unchanged). Bridge subscriptions are wired inside each handle's onEnter (deferred via setTimeout(0) so the freshly-stamped child is queryable) and torn down in onLeave + ngOnDestroy because the steppers component instantiates only the active step's child at a time. Step 4's submit awaits step4.updatePolicy and translates the legacy redirect result into an explicit Router.navigateByUrl(parentUrl).
FWT-959 framework fix. SteppersComponent.setActive() invoked step.onEnter() directly — bypassing pOnEnter, the helper added in Part 1 (commit 0204495) that prefers signalHandle.onEnter over the legacy @input default no-op. The result: Shape 3 wizards that wired bridge subscriptions or child onEnter delegation through signalHandle.onEnter never received the entry callback, leaving their bridges dead and child onEnter side-effects unfired. Bug was masked by spec smoke-tests which don't navigate between steps. Replace the two raw step.onEnter(this.enterData) call-sites in setActive() with step.pOnEnter(this.enterData). pOnEnter's internal null-check supersedes the now-redundant if (step.onEnter) guards. Side-effect: legacy wizards with [destructiveStep]="true" now run the 1-second busy-flag visual delay on step entry (the long- documented intent of pOnEnter). Affects two pre-migrated consumers (application-delete, detach-service-instance); behaviour matches the design comment in step.component.ts:299-318. invokeLeave (line 265) and invokeNext (line 148) were already wired correctly — only onEnter needed routing.
Production ng build surfaced three TS strict errors that vitest's permissive TS pass missed (per feedback_gate_ts_strict_gap.md): - manage-users:116 — drop result.message access; modify.onNext() returns a narrower union without message field, and the throw doesn't need it. - kube-config-registration:92 — pass (0, null as any) to importer.onNext; StepOnNextFunction signature requires (index, step) arguments even though the importer's implementation ignores them. - upgrade-release:184 — annotate map((): void => undefined) so the function expression has an explicit return type. All three live in already-shipped FWT-959 Part 2 commits. Catching them now before queuing more migrations on top.
FWT-959 Part 2 (Partition B). 3-step CF/org/space → app-name → route flow. Cross-step state stays in CfOrgSpaceDataService + ngrx (SetCFDetails / SetNewAppName) — children read/write through the existing observable surface. Each step's handle delegates to the matching child via ViewChild setters that wire bridge subscriptions the moment the lazily-stamped child becomes available and tear them down when the user navigates away. step1's blocked field mirrors cfOrgSpaceService.isLoading$ via toSignal(); step2's setter calls v.onEnter() to replicate the legacy [onEnter] binding so the appName control re-validates on entry; step3's setter seeds and mirrors setDomainHost.statusChanges into a local valid signal. step3.onNext keeps dispatching its own RouterNav on success — no explicit Router.navigate replacement needed.
FWT-959 Part 2 (Partition B). Up-to-6-step source-select → source- config → upload → options → deploy flow. Step 1 (Cloud Foundry) stays gated by the existing @if (!appGuid) template guard; steps 2_1 and 2_2 keep their legacy [hidden] / [skip] bindings (the binding for step2_2.skip$ silently passes undefined today and is preserved as-is). Each step uses a ViewChild setter so bridge subscriptions wire when the lazily-stamped child appears; step3's showBusy is derived from the same disablePrevious signal that mirrors status.deploying — no extra child API needed. The signal-handle submit() contract drops the legacy enterData data channel, so the parent now captures step2_2's deployer into pendingDeployer and forwards it into step3.onEnter(deployer) on the next ViewChild fire — preserving the file-upload deployer hand-off the legacy stepper performed framework-side.
FWT-959 Part 2 (Partition B, final). 5-step service flow (CF → service → plan → bind → instance) plus user-provided 3-step variant. Cross-step state stays in CsiGuidsService / CsiModeService + SetCreateServiceInstance* ngrx actions — children unchanged. Mode branching (showSelectCf / showSelectService / showBindApp) remains gated by @if blocks so no skipIf needed; bind-app's skipApps$ is mirrored into the handle's skipIf signal. ViewChild setters wire bridge subs on lazy step activation: selectCF.validate, selectService.isFetching$, specifyDetails. validate / serviceInstancesInit$. selectService / selectPlan / bindApp / supd already expose validate as a Signal so handles just computed-wrap the optional ViewChild reference. The legacy [onEnter]="resetStoreData" on the CF step is replicated inside the selectCF setter. The select-plan → bind-app / specify-details plan hand-off (which the legacy stepper carried via enterData) is captured parent-side in selectedPlan and forwarded by the downstream setters — gated by pending*Enter flags so the eager initial ViewChild fire doesn't call onEnter with stale state.
FWT-959 Part 2 follow-up. The migration of deploy-application dropped the data channel between step2's source-select submit and step2_2's source-upload onEnter. Legacy stepper relayed step2.onNext.data (a FileScannerInfo) into step2_2.onEnter via the framework enterData field; signal-handle submit() has no return-value channel, and the agent assumed step2_2.onEnter could ignore the data — it cannot. The unprimed deployer.fsFileInfo throws at deployer.fsFileInfo.root during file-upload deploys (deploy-application-deployer.ts:486). Capture step2.onNext.data into a parent-side pendingFsFileInfo and forward via the step2_2 ViewChild setter — same pattern already in place for the step2_2 → step3 deployer hand-off (pendingDeployer). git-url and docker-image source paths bypass step2_2 so leaving the field undefined in those flows is correct.
FWT-959 Part 2 follow-up. The original migration wired bridge subscriptions inside signalHandle.onEnter, deferring the ViewChild read via setTimeout(0) so the freshly-stamped child became readable. Macrotasks aren't tracked by zoneless Angular CD — works in practice because cdr.markForCheck inside the subscriber compensates, but the shape is fragile under future CD changes. Replace with @ViewChild setters (same pattern used by Partition B's deploy-application / add-service-instance / create-application). The setter fires whenever the ref becomes available — synchronously right after the *ngTemplateOutlet swap — and again with undefined on step exit, so bridge subscription lifecycle is automatic. Drops the setTimeout indirection and the duplicate teardown logic between onLeave and ngOnDestroy.
FWT-959 Part 2 follow-up. add-service-instance declared two boolean fields that are never assigned or read anywhere in the codebase — inherited from a pre-migration state. The template's step gating reads modeService.viewDetail.show* directly, not these fields.
FWT-959 Part 2 follow-up. validateSub (subscribed inside the helper.hasUpgrade() callback to mirror validate$ into the version- step handle's signal) was never torn down — listConfig.versionsData Source.selectedRows$ is a hot stream that would keep emitting after component destroy until GC. Implement OnDestroy + unsubscribe.
FWT-959 Part 2 follow-up. The migration hardcoded /endpoints inside the review step's submit() Router.navigate call to mirror the template's <app-steppers cancel="/endpoints"> attribute. Drift risk if the cancel attr is ever changed. Extract a readonly cancelUrl field and bind both the stepper attr and the navigate call to it — matches manage-users / remove-user's defaultCancelUrl pattern.
FWT-959 Part 2 follow-up. The block comment claimed the conditional first step was surfaced as a hidden field on the SignalStepHandle. Actually it's gated by the @if (setUsernames) template guard; setUsernames is set in the constructor and never flips, so a static template gate suffices and no signal-handle hidden field exists. Comment-only fix.
The gate (bun run gate-check) was lint + vitest, which let strict-mode TS errors caught only by ng build's Angular compiler slip through to make build (and CI). Slice 3 #5 surfaced this when a V2-shape guard's bail-out path returned the wrong narrowed type — green gate, red production build. Append `bun run build` to gate-check so the gate now matches what the deploy pipeline actually rejects. Adds ~60-90s per gate run; worth it to fail fast before push. Update Makefile help text to reflect the new step.
Adds a 'radio' kind to SignalListColumn with SignalListRadioBinding exposing selectedKey (WritableSignal<string|null>) plus optional isDisabled. Renders a single-select radio in both card and table modes. AddRoutesComponent's map-existing flow needs a tab-scoped single-select picker; the stepper-local cf-map-routes config wires into this slot in commit #4.
Mirrors slice 3's removeRoute: appends a freshly-attached route without re-fetching. Mutates BOTH route signals so each consumer view updates synchronously — _routes drives the Routes tab list (slice 3 read path), _appDetail.app.routes drives the build-tab Routes count metadata-item. Idempotent on guid match per signal; no-op on _routes when null so the in-flight slice 3 drain isn't raced. Specs cover both signals plus partial-state convergence.
Adds three sync verbs to the slice-3 service: attachRoute (PUT
/pp/v1/cf/apps/{cnsi}/{app}/routes/{route}, empty 200),
createRoute (POST /pp/v1/cf/routes/{cnsi}, returns StRoute),
and createAndAttachRoute which composes them. Orphan-on-attach
fail surfaces a thrown Error with the orphan StRoute attached;
caller (AddRoutesComponent commit #5) re-throws verbatim so the
stepper snackbar displays the orphan GUID + URL.
Tab/page-scoped @Injectable() service that builds the SignalList config for the map-existing-route picker. Drains via the new /pp/v1/cf/routes/{cnsi}?space_guids=... wire (commit #4.5). Columns: radio + host + domain + path + port + apps. Disables rows already mapped to the current app by reading AppDetailDataService.routes() lazily inside isDisabled.
listAllRoutes now forwards a space_guids filter (full-list branch only; counts stays cnsi-wide as the deliberate fast path). Verified live against /v3/routes (3.180.0) — adepttech served 1 route filtered vs 47 unfiltered. toStRoute reads inline r.Destinations from the list response and populates AppGUIDs directly; deletes the populateRouteDestinations helper which was N+1 against CAPI for no reason. Tests cover counts ignoring the filter, full-list forwarding the URL-encoded comma-delimited guids, and zero /destinations path hits in the route DTO mapper.
Replaces ngrx cfEntityCatalog.route.api.create + .application.api.assignRoute with AppRouteActionsService verbs. Map-existing flow swaps MapRoutesComponent for <app-signal-list> driven by CfMapRoutesSignalConfigService (commit #4). Mode toggle refreshes the picker on entry to 'map'. Orphan-on-attach errors re-throw verbatim so the stepper snackbar shows GUID + URL. Stepper provider list adds AppRouteActionsService and CfMapRoutesSignalConfigService; the legacy ListConfig provider is dropped (legacy guard test asserts its absence). Legacy MapRoutesComponent retired in commit #6.
Three small adjacent fixes folded together: - application.service.ts hydrates legacy data.cf via withLatestFrom on endpointEntitiesSelector so consumers reading cf?.name (e.g. build-tab CF endpoint row) display the name not the GUID. - build-tab.component.html now renders the resolved endpoint name with the GUID as fallback. - cf-entity-generator getGuid falls back to entity.guid when the V2 metadata wrapper is absent, matching the V3 flat shape that arrives via the native routes wire. - native_apps_detail.composeAppDetails now fetches the per-app routes in the same errgroup as droplet/processes/package/build/ ssh and stamps them onto StAppDetail.App.Routes, so the build tab's Routes count metadata-item populates without an extra client roundtrip. - VERSION + package.json roll to dev.67 to cover the bundle.
Removes 11 files now orphaned by the signal-native flow: Map-existing flow (replaced by <app-signal-list> in #5): map-routes/map-routes.component.{ts,html,spec.ts} cf-app-map-routes-list-config.service.{ts,spec.ts} Dead delete-app routes picker (replaced by AppRoutesPickerComponent in slice 3, never deleted): delete-app-routes/delete-app-routes.component.{ts,html,spec.ts} delete-app-routes/app-delete-routes-list-config.service.ts Shared base classes whose last consumers retired above: cf-app-routes-list-config-base.ts cf-app-routes-data-source.ts The two leaf folders (map-routes/, delete-app-routes/) are removed along with their contents. Migration-history comments in add-routes.component.ts and add-route-stepper.component.{ts,spec.ts} are kept — they document the V2 → signal-native swap.
CF v3's rendered route URL is bare (host.domain[/path] or domain:port for TCP). Bucket B started populating StAppDetail.app.routes from the v3 list response — before that, the app-detail signal had no routes at all and AppDetailDataService.url() returned null, leaving the action-bar Visit button greyed out. Now it returns the bare URL, which the <a [href]> on application-action-bar.component.html:61 interprets as a relative path — clicking opens localhost:5440/<bare-host>/ in a new tab and 404s. Prepend https:// at AppDetailDataService.url() unless the URL already carries a scheme. Modern CF deployments serve all HTTP routes over TLS, matching the legacy getRoute(... browsable=true, secure=true ...) behaviour. Specs updated to assert the prefix and the no-double-prepend case.
After CardAppInstancesComponent.scaleApp() resolves, refresh both the app-detail signal (carries the new desired count → denominator) and the stats signal (carries the per-container state list → running count + total) in parallel. The previous code dispatched a legacy ngrx action (cfEntityCatalog.appStats.actions.getMultiple) which doesn't feed the signal-native _appDetail / _stats sources, so both the Status card "M/N running" line and the Instances card "M/N" display lagged the focus-poll cadence by up to 5 seconds and could disagree (e.g. status shows 1/2 while instances shows 1/1) until the poll caught up. Explicit signal refreshes restore the legacy "matches quickly" behaviour. cfEntityCatalog import dropped — the component no longer touches ngrx for any verb.
Adds a new SignalList column kind 'gauge' that renders an <app-usage-gauge> per cell — a horizontal bar that fills with the 0..1 fraction and turns yellow at warningAt / red at errorAt. SignalListGaugeBinding<T> exposes a value extractor, optional valueText override (defaults to percentage), and threshold constants. Wired into both card-mode and table-mode renders; truncate guards updated to keep the gauge cell from being clipped. CfAppInstancesSignalConfigService switches the Memory, Disk, and CPU columns from plain text to kind: 'gauge' with the legacy warningAt=0.8 / errorAt=0.9 thresholds. Memory and Disk derive a 0..1 fraction from used/quota via a small helper that returns 0 when quota is missing; CPU usage from CF arrives as a fraction already and is capped at 1 to absorb the occasional >100% spike. The render() callback still emits the human-readable "14 / 128 MB" string for the filter/title fallback, so list filtering by substring keeps working unchanged. Restores the V2 instances list visual that the slice-2 migration lost when it dropped <app-table-cell-usage>.
krutten
approved these changes
May 4, 2026
krutten
left a comment
Contributor
There was a problem hiding this comment.
Merging for next DEV build
norman-abramovitz
marked this pull request as ready for review
May 4, 2026 22:08
This was referenced May 5, 2026
nabramovitz
added a commit
to nabramovitz/stratos
that referenced
this pull request
Jul 1, 2026
getNativeOrgDomains filtered CF v3's organization_guids result down to OwningOrgGUID==orgGuid, discarding every shared and global domain. That query mixes owned + shared domains (V2->V3 cutover, PR cloudfoundry#5308), and most orgs have no private domains of their own, so the Add Route domain dropdown ended up empty. Only exclude domains privately owned by a different org; keep global shared domains (no owner) and domains explicitly shared with this org via SharedOrgGUIDs. Adds TestGetNativeOrgDomains_KeepsSharedAndGlobalDomains, the first coverage for this handler.
norman-abramovitz
pushed a commit
that referenced
this pull request
Jul 1, 2026
getNativeOrgDomains filtered CF v3's organization_guids result down to OwningOrgGUID==orgGuid, discarding every shared and global domain. That query mixes owned + shared domains (V2->V3 cutover, PR #5308), and most orgs have no private domains of their own, so the Add Route domain dropdown ended up empty. Only exclude domains privately owned by a different org; keep global shared domains (no owner) and domains explicitly shared with this org via SharedOrgGUIDs. Adds TestGetNativeOrgDomains_KeepsSharedAndGlobalDomains, the first coverage for this handler.
Bartan89
pushed a commit
to Bartan89/stratos
that referenced
this pull request
Jul 3, 2026
getNativeOrgDomains filtered CF v3's organization_guids result down to OwningOrgGUID==orgGuid, discarding every shared and global domain. That query mixes owned + shared domains (V2->V3 cutover, PR cloudfoundry#5308), and most orgs have no private domains of their own, so the Add Route domain dropdown ended up empty. Only exclude domains privately owned by a different org; keep global shared domains (no owner) and domains explicitly shared with this org via SharedOrgGUIDs. Adds TestGetNativeOrgDomains_KeepsSharedAndGlobalDomains, the first coverage for this handler.
This was referenced Jul 31, 2026
This was referenced Nov 12, 2026
norman-abramovitz
pushed a commit
that referenced
this pull request
Nov 12, 2026
The move to the signal list (#5308) made the Name column of the Organization Quotas and Space Quotas lists plain text, so neither the table name nor the card title opened the quota's page. Both columns link again, to the paths the old quota cell used.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-to-end V2 → V3 cutover for cf-stratos. The Stratos backend's CF interactions on the user-traffic path are now V3-only; the frontend reads from V3-native Jetstream handlers (
/pp/v1/cf/...) returning Stratos-shape DTOs (StApp,StOrg,StSpace,StServiceInstance,StServiceBroker,StStack,StBuildpack,StRoute,StRole,StUser, …) over single-resource and bounded-pagination endpoints.Supersedes #5307. Every commit on #5307 is contained in this branch — ancestry confirmed. Once #5308 merges, #5307 will be closed. Dev iterations v5.0.0-dev.10 → dev.46.
What's in here
Signal-native infrastructure
EndpointDataService/OrgDataService/EndpointDataRegistry— per-endpoint and per-org signal stores with parallel fetchEndpointDataShim— write-through bridge from Stratos-shape to NgRx for incremental migrationPage migrations to signal-native list
ListStateStore.bind()SignalListConfig framework additions
headerActionsslot for page-level actions (Manage Roles, Invite User…)maxVisiblefor cells with hundreds of segmentsApp wall improvements
getNativeAppsenriches the default page with web-process memoryRead-side V3-native sweep (A0 → A6)
Read paths for ~14 entities migrated to V3-native Jetstream handlers:
?return=countsfor bounded countsSingle-resource endpoints alongside list endpoints (no auto-drain),
?guids=first-class, uniform paginated envelope. Wire contract follows the six principles forced by the spaces-504 incident (no unbounded list responses, single-resource alongside list,?guids=first-class, uniform envelope, no auto-drain, filters narrow at CAPI).Write-side V3 cutover
stratosjobs.Tracker+JobTranslatorinterface,RunFastPathhybrid sync/handoff wrapper (/pp/v1/stratosjobs/{id}for client polling)CFJobTranslatorfor single CF v3 jobs (/v3/jobs/{guid})/v2/apps/{guid}→/v3/apps/{guid}(last user-traffic v2 callsite removed)A8 — Restage V3 orchestration (downtime path)
Restage previously routed through a
/v2/apps/{guid}/restagepassthrough — the only atomic restage CF ever shipped. CF v3 has no atomic restage; cf-cli v8.18.3 implements it as a 7-step composition: package lookup → build create → build poll → set droplet → stop → start → instance poll. This branch implements that composition behind the Stratos async-job contract:RestageJobTranslatoradvances the state machine by one stage per Tracker.Refresh poll (so HTTP handlers stay short while staging takes minutes)advanceRestage(ctx, client, ref)— orchestrator state machine, downtime pathgetNewestReadyPackage,createBuildForPackage,pollBuildUntilTerminal,setCurrentDroplet,stopApp,startApp,getWebProcessGUID+pollInstancesUntilRunningcommand/v7/restage_command.go+command/v7/shared/app_stager.go. When upstream changes, the maintainer follows the numbered procedure at the top ofnative_apps_restage_v3.go.Wire shape preserved — frontend's existing
writeWithJob(restage)call incf-apps-signal-config.service.tsstill works with the new V3 backend. Rolling/canary strategy is accepted at the wire but not yet honored by the orchestrator (deferred — see "Out of scope").A9 — userinvite v2 → v3
Four CF API calls in the userinvite plugin migrated:
POST /v2/usersPOST /v3/usersPUT /v2/organizations/{org}/users/{user}POST /v3/roles {type:"organization_user"}PUT /v2/spaces/{space}/{role}/{user}POST /v3/roles {type:"space_<role>"}GET /v2/organizations/{org}/user_roles?q=user_guid:XGET /v3/roles?organization_guids&user_guidsV2
CF-UaaIdTakenidempotency check replaced with V3UniquenessError(code 10016 + title/detail substring fallback). Bug fix:processUserInviteno longer callsAssociateUserWithOrgtwice (:=shadowing in the legacy guardedif).Backend V3 cutover for the frontend's
/v2/infoNew native handler
GET /pp/v1/cf/info/:cnsiGuidcomposing/v3/info+ the unversioned API root/(withLink.Metaextracted from the latter). Returns a Stratos-shape JSON whosesnake_casefields mirror the legacyICfV2Infoso the frontend cutover is a pure URL swap —cloud-foundry.effects.ts:40now calls/pp/v1/cf/info/{cnsi}instead of/pp/v1/proxy/v2/info. No frontend consumer (CF Summary card,hasSSHAccess$) changes.The dual-probe of
/v2/infoand/v3/infoincloudfoundry/main.gostays — it's intentional capability detection (V2-only vs V3-only vs both), not an unmigrated callsite. Documented inline.A7 — cf-cli v8 bump
code.cloudfoundry.org/cli/v8 v8.18.2 → v8.18.3. Indirect dependency tracking — cf-cli v8 itself uses capi v3 internally, so updating it tightens our V3 alignment without direct API changes.FWT-18 — User Role + Users count
CF Summary user-role and users-count derived from V3
StUsersnapshot rather than ngrx pagination drain.Bug fixes
endpointDataServiceheld in a plain field;orgs/spacescomputeds tracked only the first endpoint's signal forever. Fix wraps the field inWritableSignalso the computeds re-track on CNSI swap. Affectscf-orgs-signal-configandcf-spaces-signal-config.signal-list.component.html— favorite/actions columns share an empty header, so tracking bycol.headerproduced duplicate""keys on every CD pass. Switched all six call sites tocol.key. Console quieter, rendering noticeably faster.[favorite]race with[entityConfig]— converted favorite to a setter that records whether the parent bound it, and moved the entity-monitor fallback subscription tongOnInitso the race no longer overwrites a parent-supplied favorite via the entity-stamped cfGuid.pageSizeOptionslist (table-mode 25 was leaving the dropdown showing 6 while 25 cards rendered).authTypesdefault toUsernamePassword + Noneso plugin-package endpoints (metrics, autoscaler) render their Connect step instead of crashing onObject.keys(undefined).transformutility on modal content was creating a containing block and breakingposition:fixedmath).markForCheck'd after busy resets so retry works under OnPush + zoneless.Dependency bumps
@stratosui/devkit→ Angular 21 toolchaingolang.org/x/cryptoacross three plugins (clears 6 moderates)code.cloudfoundry.org/cli/v8 v8.18.2 → v8.18.3(A7)github.com/norman-abramovitz/fw-capi/v3 v3.216.4-fix-apps-delete.6 → .7(Link.Meta support)fw-capi dependency
This branch consumes
norman-abramovitz/fw-capi/v3 v3.216.4-fix-apps-delete.7. Two upstream PRs againstfivetwenty-io/capicarry the fork's changes:*Jobfrom Location header on Delete/Scale/Start/Stop/Restart)Link.Meta map[string]interface{}(used by/pp/v1/cf/infoto readapp_ssh.meta.host_key_fingerprintetc.)Once those merge, a follow-up Stratos PR will drop the
replacedirective ingo.modand reference upstream directly.Test plan
make check gate) — frontend lint + vitest (1442 tests across 588 files), backendgo vet+go test ./...dev.46deployed): connection, navigation, list readsOut of scope (queued for follow-up tickets)
deployment_create+deployment_pollorchestrator stages. Wire acceptsstrategy: rolling|canarybut orchestrator only drives the downtime path today.POST /v3/apps/{guid}/actions/rollbackhandler.CFEndpointMetadata.SupportsV3._meta.unavailableemission policy — deferred until ≥3 concrete tristate cases force a shape decision (today onlyStServiceBroker.authUsername).make check gate, contract fixtures, optionalmake check gate-extraintegration target. Tracked inKS docs/2026-04-30-gate-improvements-assessment.md.Notes for review
git logto find what's gone..7tag from the fork; upstream merges land via follow-up.fw-capi version pinning
The
.7tag is onnorman-abramovitz/fw-capi:fix-apps-delete-returns-job. After the upstream PRs merge, the plan is:replaceto the main-derived tag (eliminates "Stratos points at a feature branch" fragility).fivetwenty-io/capihas the changes, drop thereplacedirective entirely.Tracked separately so this PR's review isn't gated on upstream PR acceptance.