History

feat(plugins): consolidate icon URL handling

- Remove configInput parameter from toConfigSource and toConfig - Centralize iconUrl handling in scopeConfigSourceBaseFromSource - Remove configuredIconUrlFromConfigInput utility (moved to source-core) - Simplify iconUrl handling with optional chaining trim pattern - Re-export getFaviconUrlForRemoteUrl from source-core in plugin shared modules

Saatvik Arya committed 7 months ago

Commit 9dcd6461 parent 27ebe8610 files · +32 −66Browse files

Unified · Split

10 files
  1. packages/platform/sdk/src/plugins.ts +6 −11
  2. packages/platform/sdk/src/runtime/sources/source-definitions.ts +2 −6
  3. packages/platform/sdk/src/runtime/sources/source-icon.ts +2 −11
  4. packages/platform/sdk/src/runtime/sources/source-store/config.ts +5 −2
  5. packages/platform/sdk/src/runtime/sources/source-store/plugin-scope-config.ts +0 −9
  6. plugins/graphql/sdk/index.ts +5 −7
  7. plugins/graphql/shared/index.ts +1 −0
  8. plugins/mcp/sdk/index.ts +5 −13
  9. plugins/mcp/shared/index.ts +1 −0
  10. plugins/openapi/sdk/index.ts +5 −7
packages/platform/sdk/src/plugins.ts+6−11
@@ -294,7 +294,6 @@ export type ExecutorSourcePluginDefinition< · 293 unmodified lines
294 toConfig: (input: {294 toConfig: (input: {
295 source: ExecutorSource;295 source: ExecutorSource;
296 stored: TStored;296 stored: TStored;
297 configSource: ExecutorScopeConfigSource | null;
298 }) => TSourceConfig;297 }) => TSourceConfig;
299 remove?: (input: {298 remove?: (input: {
300 source: ExecutorSource;299 source: ExecutorSource;
@@ -305,7 +304,6 @@ export type ExecutorSourcePluginDefinition< · 4 unmodified lines
305 toConfigSource: (input: {304 toConfigSource: (input: {
306 source: ExecutorSource;305 source: ExecutorSource;
307 stored: TStored;306 stored: TStored;
308 configInput: TConnectInput | TSourceConfig | null;
309 }) => ExecutorScopeConfigSource;307 }) => ExecutorScopeConfigSource;
310 recoverStored: (input: {308 recoverStored: (input: {
311 source: ExecutorSource;309 source: ExecutorSource;
@@ -610,7 +608,6 @@ const persistSourceScopeConfig = < · 298 unmodified lines
610 input: {608 input: {
611 source: ExecutorSource;609 source: ExecutorSource;
612 stored: TStored;610 stored: TStored;
613 configInput: TConnectInput | TSourceConfig | null;
614 },611 },
615): Effect.Effect<void, Error, ScopeConfigStore> =>612): Effect.Effect<void, Error, ScopeConfigStore> =>
616 definition.scopeConfig613 definition.scopeConfig
@@ -619,12 +616,12 @@ const persistSourceScopeConfig = < · 2 unmodified lines
619 const scopeConfigStore = yield* ScopeConfigStore;616 const scopeConfigStore = yield* ScopeConfigStore;
620 const loadedConfig = yield* scopeConfigStore.load();617 const loadedConfig = yield* scopeConfigStore.load();
621 const projectConfig = cloneJson(loadedConfig.projectConfig ?? {});618 const projectConfig = cloneJson(loadedConfig.projectConfig ?? {});
619
622 const sources = {620 const sources = {
623 ...projectConfig.sources,621 ...projectConfig.sources,
624 [input.source.id]: scopeConfig.toConfigSource({622 [input.source.id]: scopeConfig.toConfigSource({
625 source: input.source,623 source: input.source,
626 stored: input.stored,624 stored: input.stored,
627 configInput: input.configInput,
628 }),625 }),
629 };626 };
630 627
@@ -737,12 +734,12 @@ const createExecutorSourcePluginApi = < · 106 unmodified lines
737 734
738 const scopeConfigStore = yield* ScopeConfigStore;735 const scopeConfigStore = yield* ScopeConfigStore;
739 const loadedConfig = yield* scopeConfigStore.load();736 const loadedConfig = yield* scopeConfigStore.load();
737 const iconUrl = loadedConfig.config?.sources?.[source.id]?.iconUrl;
740 738
741 return definition.source.toConfig({739 return {
742 source,740 ...definition.source.toConfig({ source, stored }),
743 stored,741 ...(iconUrl ? { iconUrl } : {}),
744 configSource: loadedConfig.config?.sources?.[source.id] ?? null,742 };
745 });
746 }),743 }),
747 createSource: (input) =>744 createSource: (input) =>
748 Effect.gen(function* () {745 Effect.gen(function* () {
@@ -759,7 +756,6 @@ const createExecutorSourcePluginApi = < · 10 unmodified lines
759 yield* persistSourceScopeConfig(definition, {756 yield* persistSourceScopeConfig(definition, {
760 source,757 source,
761 stored: created.stored,758 stored: created.stored,
762 configInput: input,
763 });759 });
764 760
765 return yield* host.sources.refreshCatalog(source.id);761 return yield* host.sources.refreshCatalog(source.id);
@@ -784,7 +780,6 @@ const createExecutorSourcePluginApi = < · 18 unmodified lines
784 yield* persistSourceScopeConfig(definition, {780 yield* persistSourceScopeConfig(definition, {
785 source: saved,781 source: saved,
786 stored: updated.stored,782 stored: updated.stored,
787 configInput: input.config,
788 });783 });
789 784
790 return yield* host.sources.refreshCatalog(saved.id);785 return yield* host.sources.refreshCatalog(saved.id);
packages/platform/sdk/src/runtime/sources/source-definitions.ts+2−6
@@ -126,9 +126,7 @@ export const normalizeSourceForCreate = (input: { · 125 unmodified lines
126 status: input.source.status,126 status: input.source.status,
127 enabled: input.source.enabled,127 enabled: input.source.enabled,
128 namespace: trimOrNull(input.source.namespace),128 namespace: trimOrNull(input.source.namespace),
129 ...(trimOrNull(input.source.iconUrl)129 ...(input.source.iconUrl?.trim() ? { iconUrl: input.source.iconUrl.trim() } : {}),
130 ? { iconUrl: trimOrNull(input.source.iconUrl) ?? undefined }
131 : {}),
132 createdAt: input.now,130 createdAt: input.now,
133 updatedAt: input.now,131 updatedAt: input.now,
134 });132 });
@@ -141,9 +139,7 @@ export const normalizeSourceForSave = (input: { · 6 unmodified lines
141 ...input.source,139 ...input.source,
142 name: input.source.name.trim(),140 name: input.source.name.trim(),
143 namespace: trimOrNull(input.source.namespace),141 namespace: trimOrNull(input.source.namespace),
144 ...(trimOrNull(input.source.iconUrl)142 ...(input.source.iconUrl?.trim() ? { iconUrl: input.source.iconUrl.trim() } : {}),
145 ? { iconUrl: trimOrNull(input.source.iconUrl) ?? undefined }
146 : {}),
147 updatedAt: input.now,143 updatedAt: input.now,
148 });144 });
149 145
packages/platform/sdk/src/runtime/sources/source-icon.ts+2−11
@@ -1,18 +1,9 @@
1import {1import {
2 getFaviconUrlForRemoteUrl,2 getFaviconUrlForRemoteUrl,
3 isRecord,
3} from "@executor/source-core";4} from "@executor/source-core";
4 5
5const trimOrNull = (value: string | null | undefined): string | null => {6import { trimOrNull } from "./source-store/config";
6 if (value == null) {
7 return null;
8 }
9
10 const trimmed = value.trim();
11 return trimmed.length > 0 ? trimmed : null;
12};
13
14const isRecord = (value: unknown): value is Record<string, unknown> =>
15 typeof value === "object" && value !== null && !Array.isArray(value);
16 7
17export const resolveSourceIconUrl = (input: {8export const resolveSourceIconUrl = (input: {
18 configuredIconUrl?: string | null;9 configuredIconUrl?: string | null;
packages/platform/sdk/src/runtime/sources/source-store/config.ts+5−2
@@ -47,6 +47,9 @@ export const scopeConfigSourceBaseFromSource = (input: { · 46 unmodified lines
47 ...(trimOrNull(input.source.namespace) !== trimOrNull(input.source.id)47 ...(trimOrNull(input.source.namespace) !== trimOrNull(input.source.id)
48 ? { namespace: input.source.namespace ?? undefined }48 ? { namespace: input.source.namespace ?? undefined }
49 : {}),49 : {}),
50 ...(input.source.iconUrl?.trim()
51 ? { iconUrl: input.source.iconUrl.trim() }
52 : {}),
50 ...(input.source.enabled === false ? { enabled: false } : {}),53 ...(input.source.enabled === false ? { enabled: false } : {}),
51});54});
52 55
@@ -58,8 +61,8 @@ export const scopeConfigSourceFromSource = (input: { · 5 unmodified lines
58 ...scopeConfigSourceBaseFromSource({61 ...scopeConfigSourceBaseFromSource({
59 source: input.source,62 source: input.source,
60 }),63 }),
61 ...(trimOrNull(input.existingConfig?.iconUrl)64 ...(input.existingConfig?.iconUrl?.trim()
62 ? { iconUrl: trimOrNull(input.existingConfig?.iconUrl) ?? undefined }65 ? { iconUrl: input.existingConfig.iconUrl.trim() }
63 : {}),66 : {}),
64 kind: input.source.kind as ExecutorScopeConfigSource["kind"],67 kind: input.source.kind as ExecutorScopeConfigSource["kind"],
65 ...(input.existingConfig?.config !== undefined68 ...(input.existingConfig?.config !== undefined
packages/platform/sdk/src/runtime/sources/source-store/plugin-scope-config.ts+0−9
@@ -29,19 +29,10 @@ export const createPluginScopeConfigEntrySchema = < · 28 unmodified lines
29export const pluginScopeConfigSourceFromConfig = <TConfig>(input: {29export const pluginScopeConfigSourceFromConfig = <TConfig>(input: {
30 source: Source;30 source: Source;
31 config: TConfig;31 config: TConfig;
32 iconUrl?: string | null;
33}): ExecutorScopeConfigSource => ({32}): ExecutorScopeConfigSource => ({
34 ...scopeConfigSourceBaseFromSource({33 ...scopeConfigSourceBaseFromSource({
35 source: input.source,34 source: input.source,
36 }),35 }),
37 ...(input.iconUrl ? { iconUrl: input.iconUrl } : {}),
38 kind: input.source.kind as ExecutorScopeConfigSource["kind"],36 kind: input.source.kind as ExecutorScopeConfigSource["kind"],
39 config: cloneJson(input.config),37 config: cloneJson(input.config),
40});38});
41
42export const configuredIconUrlFromConfigInput = (input: unknown): string | null =>
43 input && typeof input === "object" && "iconUrl" in input
44 ? (typeof input.iconUrl === "string"
45 ? input.iconUrl.trim() || null
46 : null)
47 : null;
plugins/graphql/sdk/index.ts+5−7
@@ -23,7 +23,6 @@ import { · 22 unmodified lines
23} from "@executor/platform-sdk/plugins";23} from "@executor/platform-sdk/plugins";
24import {24import {
25 SecretMaterialResolverService,25 SecretMaterialResolverService,
26 configuredIconUrlFromConfigInput,
27 createPluginScopeConfigEntrySchema,26 createPluginScopeConfigEntrySchema,
28 pluginScopeConfigSourceFromConfig,27 pluginScopeConfigSourceFromConfig,
29 provideExecutorRuntime,28 provideExecutorRuntime,
@@ -320,10 +319,8 @@ const storedSourceDataFromInput = ( · 290 unmodified lines
320const sourceConfigFromStored = (319const sourceConfigFromStored = (
321 source: Source,320 source: Source,
322 stored: GraphqlStoredSourceData,321 stored: GraphqlStoredSourceData,
323 configSource: { iconUrl?: string } | null,
324): GraphqlSourceConfigPayload => ({322): GraphqlSourceConfigPayload => ({
325 name: source.name,323 name: source.name,
326 ...(configSource?.iconUrl ? { iconUrl: configSource.iconUrl } : {}),
327 endpoint: stored.endpoint,324 endpoint: stored.endpoint,
328 defaultHeaders: stored.defaultHeaders,325 defaultHeaders: stored.defaultHeaders,
329 auth: stored.auth,326 auth: stored.auth,
@@ -385,6 +382,7 @@ export const graphqlSdkPlugin = (options: { · 55 unmodified lines
385 endpoint: input.endpoint,382 endpoint: input.endpoint,
386 title: input.name,383 title: input.name,
387 }),384 }),
385 ...(input.iconUrl?.trim() ? { iconUrl: input.iconUrl.trim() } : {}),
388 },386 },
389 stored: storedSourceDataFromInput(input),387 stored: storedSourceDataFromInput(input),
390 }),388 }),
@@ -396,18 +394,18 @@ export const graphqlSdkPlugin = (options: { · 5 unmodified lines
396 endpoint: config.endpoint,394 endpoint: config.endpoint,
397 title: config.name,395 title: config.name,
398 }),396 }),
397 ...(config.iconUrl?.trim() ? { iconUrl: config.iconUrl.trim() } : {}),
399 },398 },
400 stored: storedSourceDataFromInput(config),399 stored: storedSourceDataFromInput(config),
401 }),400 }),
402 toConfig: ({ source, stored, configSource }) =>401 toConfig: ({ source, stored }) =>
403 sourceConfigFromStored(source, normalizeStoredSourceData(stored), configSource),402 sourceConfigFromStored(source, normalizeStoredSourceData(stored)),
404 },403 },
405 scopeConfig: {404 scopeConfig: {
406 toConfigSource: ({ source, stored, configInput }) =>405 toConfigSource: ({ source, stored }) =>
407 pluginScopeConfigSourceFromConfig({406 pluginScopeConfigSourceFromConfig({
408 source,407 source,
409 config: normalizeStoredSourceData(stored),408 config: normalizeStoredSourceData(stored),
410 iconUrl: configuredIconUrlFromConfigInput(configInput),
411 }),409 }),
412 recoverStored: ({ config, loadedConfig }) =>410 recoverStored: ({ config, loadedConfig }) =>
413 graphqlStoredSourceDataFromLocalConfig({411 graphqlStoredSourceDataFromLocalConfig({
plugins/graphql/shared/index.ts+1−0
@@ -11,6 +11,7 @@ import { · 10 unmodified lines
11 11
12export { getFaviconUrlForRemoteUrl } from "@executor/source-core";12export { getFaviconUrlForRemoteUrl } from "@executor/source-core";
13 13
14
14export const GraphqlConnectionAuthSchema = Schema.Union(15export const GraphqlConnectionAuthSchema = Schema.Union(
15 Schema.Struct({16 Schema.Struct({
16 kind: Schema.Literal("none"),17 kind: Schema.Literal("none"),
plugins/mcp/sdk/index.ts+5−13
@@ -31,7 +31,6 @@ import { · 30 unmodified lines
31 defineExecutorSourcePlugin,31 defineExecutorSourcePlugin,
32} from "@executor/platform-sdk/plugins";32} from "@executor/platform-sdk/plugins";
33import {33import {
34 configuredIconUrlFromConfigInput,
35 createPluginScopeConfigEntrySchema,34 createPluginScopeConfigEntrySchema,
36 pluginScopeConfigSourceFromConfig,35 pluginScopeConfigSourceFromConfig,
37 SecretMaterialDeleterService,36 SecretMaterialDeleterService,
@@ -339,10 +338,8 @@ const normalizeStoredSourceData = ( · 301 unmodified lines
339const sourceConfigFromStored = (338const sourceConfigFromStored = (
340 source: Source,339 source: Source,
341 stored: McpStoredSourceData,340 stored: McpStoredSourceData,
342 configSource: { iconUrl?: string } | null,
343): McpSourceConfigPayload => ({341): McpSourceConfigPayload => ({
344 name: source.name,342 name: source.name,
345 ...(configSource?.iconUrl ? { iconUrl: configSource.iconUrl } : {}),
346 endpoint: stored.endpoint,343 endpoint: stored.endpoint,
347 transport: stored.transport,344 transport: stored.transport,
348 queryParams: stored.queryParams,345 queryParams: stored.queryParams,
@@ -692,9 +689,7 @@ export const mcpSdkPlugin = ( · 343 unmodified lines
692 endpoint: input.endpoint,689 endpoint: input.endpoint,
693 command: input.command,690 command: input.command,
694 }),691 }),
695 ...(input.iconUrl && input.iconUrl.trim().length > 0692 ...(input.iconUrl?.trim() ? { iconUrl: input.iconUrl.trim() } : {}),
696 ? { iconUrl: input.iconUrl.trim() }
697 : {}),
698 },693 },
699 stored: Effect.runSync(normalizeStoredSourceData(input)),694 stored: Effect.runSync(normalizeStoredSourceData(input)),
700 }),695 }),
@@ -710,15 +705,13 @@ export const mcpSdkPlugin = ( · 9 unmodified lines
710 endpoint: stored.endpoint,705 endpoint: stored.endpoint,
711 command: stored.command,706 command: stored.command,
712 }),707 }),
713 ...(config.iconUrl && config.iconUrl.trim().length > 0708 ...(config.iconUrl?.trim() ? { iconUrl: config.iconUrl.trim() } : {}),
714 ? { iconUrl: config.iconUrl.trim() }
715 : { iconUrl: undefined }),
716 },709 },
717 stored,710 stored,
718 };711 };
719 },712 },
720 toConfig: ({ source, stored, configSource }) =>713 toConfig: ({ source, stored }) =>
721 sourceConfigFromStored(source, stored, configSource),714 sourceConfigFromStored(source, stored),
722 remove: ({ stored }) =>715 remove: ({ stored }) =>
723 Effect.gen(function* () {716 Effect.gen(function* () {
724 if (stored?.auth.kind === "oauth2") {717 if (stored?.auth.kind === "oauth2") {
@@ -731,11 +724,10 @@ export const mcpSdkPlugin = ( · 6 unmodified lines
731 }),724 }),
732 },725 },
733 scopeConfig: {726 scopeConfig: {
734 toConfigSource: ({ source, stored, configInput }) =>727 toConfigSource: ({ source, stored }) =>
735 pluginScopeConfigSourceFromConfig({728 pluginScopeConfigSourceFromConfig({
736 source,729 source,
737 config: stored,730 config: stored,
738 iconUrl: configuredIconUrlFromConfigInput(configInput),
739 }),731 }),
740 recoverStored: ({ config }) =>732 recoverStored: ({ config }) =>
741 mcpStoredSourceDataFromLocalConfig(config),733 mcpStoredSourceDataFromLocalConfig(config),
plugins/mcp/shared/index.ts+1−0
@@ -16,6 +16,7 @@ import { · 15 unmodified lines
16 16
17export { getFaviconUrlForRemoteUrl } from "@executor/source-core";17export { getFaviconUrlForRemoteUrl } from "@executor/source-core";
18 18
19
19export const McpConnectionAuthSchema = Schema.Union(20export const McpConnectionAuthSchema = Schema.Union(
20 Schema.Struct({21 Schema.Struct({
21 kind: Schema.Literal("none"),22 kind: Schema.Literal("none"),
plugins/openapi/sdk/index.ts+5−7
@@ -18,7 +18,6 @@ import { · 17 unmodified lines
18 defineExecutorSourcePlugin,18 defineExecutorSourcePlugin,
19} from "@executor/platform-sdk/plugins";19} from "@executor/platform-sdk/plugins";
20import {20import {
21 configuredIconUrlFromConfigInput,
22 createPluginScopeConfigEntrySchema,21 createPluginScopeConfigEntrySchema,
23 pluginScopeConfigSourceFromConfig,22 pluginScopeConfigSourceFromConfig,
24 SecretMaterialResolverService,23 SecretMaterialResolverService,
@@ -186,10 +185,8 @@ const createStoredSourceData = ( · 161 unmodified lines
186const configFromStoredSourceData = (185const configFromStoredSourceData = (
187 source: Source,186 source: Source,
188 stored: OpenApiStoredSourceData,187 stored: OpenApiStoredSourceData,
189 configSource: { iconUrl?: string } | null,
190): OpenApiSourceConfigPayload => ({188): OpenApiSourceConfigPayload => ({
191 name: source.name,189 name: source.name,
192 ...(configSource?.iconUrl ? { iconUrl: configSource.iconUrl } : {}),
193 specUrl: stored.specUrl,190 specUrl: stored.specUrl,
194 baseUrl: stored.baseUrl,191 baseUrl: stored.baseUrl,
195 auth: stored.auth,192 auth: stored.auth,
@@ -527,6 +524,7 @@ export const openApiSdkPlugin = ( · 331 unmodified lines
527 specUrl: input.specUrl,524 specUrl: input.specUrl,
528 title: input.name,525 title: input.name,
529 }),526 }),
527 ...(input.iconUrl?.trim() ? { iconUrl: input.iconUrl.trim() } : {}),
530 },528 },
531 stored: createStoredSourceData(input),529 stored: createStoredSourceData(input),
532 }),530 }),
@@ -538,14 +536,15 @@ export const openApiSdkPlugin = ( · 5 unmodified lines
538 specUrl: config.specUrl,536 specUrl: config.specUrl,
539 title: config.name,537 title: config.name,
540 }),538 }),
539 ...(config.iconUrl?.trim() ? { iconUrl: config.iconUrl.trim() } : {}),
541 },540 },
542 stored: createStoredSourceData(config),541 stored: createStoredSourceData(config),
543 }),542 }),
544 toConfig: ({ source, stored, configSource }) =>543 toConfig: ({ source, stored }) =>
545 configFromStoredSourceData(source, normalizeStoredSourceData(stored), configSource),544 configFromStoredSourceData(source, normalizeStoredSourceData(stored)),
546 },545 },
547 scopeConfig: {546 scopeConfig: {
548 toConfigSource: ({ source, stored, configInput }) =>547 toConfigSource: ({ source, stored }) =>
549 pluginScopeConfigSourceFromConfig({548 pluginScopeConfigSourceFromConfig({
550 source,549 source,
551 config: {550 config: {
@@ -554,7 +553,6 @@ export const openApiSdkPlugin = ( · 2 unmodified lines
554 auth: stored.auth,553 auth: stored.auth,
555 defaultHeaders: stored.defaultHeaders,554 defaultHeaders: stored.defaultHeaders,
556 },555 },
557 iconUrl: configuredIconUrlFromConfigInput(configInput),
558 }),556 }),
559 recoverStored: ({ config, loadedConfig }) =>557 recoverStored: ({ config, loadedConfig }) =>
560 openApiStoredSourceDataFromLocalConfig({558 openApiStoredSourceDataFromLocalConfig({