Files
windmill/frontend/src/lib/components/displayNameLoaders.ts
T
hugocasaandClaude Opus 5 42f489685b feat: store resource type display names and label hub integrations (#11113)
* feat: label resource types and integrations with hub display names

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: load hub integration names in the app and flow pickers

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: load hub resource type names where drawers title a type

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: store resource type display names and drop the hardcoded list

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: leave display_name out of the fork comparison

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: ignore over-long synced display names, move name loaders

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: share the hub integration list cache, backfill admins only

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep a name over a nameless duplicate, retry failed hub reads

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 11:32:50 +02:00

72 lines
2.7 KiB
TypeScript

import { get } from 'svelte/store'
import { IntegrationService, ResourceService } from '$lib/gen'
import { disableHubStore } from '$lib/stores'
import { createCache } from '$lib/utils'
import { addResourceTypeDisplayName, setHubIntegrationDisplayNames } from './resourceTypeDisplay'
/**
* Loads what `resourceTypeDisplayName` and `integrationDisplayName` read: a type's stored name for a
* surface that holds no row for it, and the hub's integration list, which every picker that needs
* it shares. Apart from `resourceTypeDisplay`, which makes no API calls so it can be unit-tested
* alone. Cached briefly: drawers and pickers reopen often, and a name rarely changes.
*/
const CACHE_MS = 60_000
const resourceTypeRowCached = createCache(
({ workspace, name }: { workspace: string; name: string }) =>
ResourceService.getResourceType({ workspace, path: name }).then(
(rt) => addResourceTypeDisplayName(rt),
() => {}
),
{ invalidateMs: CACHE_MS, maxSize: 50 }
)
/**
* Fill `resourceTypeDisplayName` for one type, for a surface titled with a type it holds no row
* for. The name is stored with the type, so this reads the row rather than the hub.
*/
export function loadResourceTypeDisplayName(workspace: string, name: string): Promise<void> {
return resourceTypeRowCached({ workspace, name })
}
/** Bumped by every failed read, so the next caller keys a fresh read rather than the rejection. */
let failedReads = 0
const hubIntegrationsCached = createCache(
({ kind }: { kind?: string; refresh: number; attempt: number }) =>
IntegrationService.listHubIntegrations({ kind }).then(
(integrations) => {
setHubIntegrationDisplayNames(integrations)
return integrations
},
(error) => {
failedReads += 1
throw error
}
),
{ invalidateMs: CACHE_MS }
)
/**
* The hub's integration list, read once a minute per `kind` however many pickers ask, recording
* each integration's name on the way. A failed read rejects, so a picker can say the hub is
* unavailable, but is not kept: the next caller reads again. `refresh` is a picker's refresh
* count, and a new value reads again too.
*/
export function listHubIntegrationsShared(kind?: string, refresh = 0) {
return hubIntegrationsCached({ kind, refresh, attempt: failedReads })
}
/**
* Fill `integrationDisplayName` for a picker whose integrations come from its own items rather
* than the hub's integration list, as the hub app and flow pickers do. Unfiltered: `kind`
* narrows by script kind, so asking for an app or a flow would name nothing.
*/
export function loadHubIntegrationDisplayNames(): Promise<void> {
if (get(disableHubStore)) return Promise.resolve()
return listHubIntegrationsShared().then(
() => {},
() => {}
)
}