diff --git a/.changeset/calm-external-apis.md b/.changeset/calm-external-apis.md new file mode 100644 index 00000000..b7b4c460 --- /dev/null +++ b/.changeset/calm-external-apis.md @@ -0,0 +1,5 @@ +--- +'@ankhorage/studio': minor +--- + +Rebuild external API authoring with trusted OpenAPI discovery, GraphQL introspection, manual REST fallback, canonical manifest persistence, operation visibility, and credential-safe endpoint testing. diff --git a/README.md b/README.md index 0e76fa95..6af97c1e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # @ankhorage/studio -![license: MIT](././paradox/badges/license.svg) ![npm: v0.11.11](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg) +![license: MIT](././paradox/badges/license.svg) ![npm: v0.11.12](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg) Standalone Studio authoring package for Ankhorage apps. diff --git a/bun.lock b/bun.lock index c8cffb6f..ecd77017 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "@ankhorage/color-theory": "^0.0.8", "@ankhorage/contracts": "^4.0.1", + "@ankhorage/data-sources": "^0.6.0", "@ankhorage/expo-runtime": "^0.0.11", "@ankhorage/infra": "^1.1.0", "@ankhorage/orchestrator": "^0.2.4", @@ -87,7 +88,7 @@ "@ankhorage/contracts": ["@ankhorage/contracts@4.0.1", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.8" } }, "sha512-8HE0Fyj8WvQwSmFLKkgqWDHBT8r47lmJMgqCOgQKRCszBP71DqoZ4X6WTVaMshrIjgm+hCkKW4JMdtGz7H9tKg=="], - "@ankhorage/data-sources": ["@ankhorage/data-sources@0.5.2", "", { "dependencies": { "@ankhorage/contracts": "^1.18.3" } }, "sha512-Z/miagAQSjYeDWH70LTRqn0e52+N58/dDWW70MLqTW1udmQxmUrJkjQ1BXocl1yAq8rcQBus+xvkJd9D40xOFw=="], + "@ankhorage/data-sources": ["@ankhorage/data-sources@0.6.0", "", { "dependencies": { "@ankhorage/contracts": "^4.0.1" } }, "sha512-PeQxyLbz6Bhb412FEzU1p3luauZJfOgYdFP29aIZyG/tP227T4dF++CdXlKifltBz5XzWyApmHH8KMJXY/Uj3Q=="], "@ankhorage/devtools": ["@ankhorage/devtools@1.2.1", "", { "dependencies": { "@eslint/js": "^10.0.1", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-unused-imports": "^4.4.1", "knip": "^6.12.2", "prettier": "^3.8.1", "typescript-eslint": "^8.24.0" }, "bin": { "ankhorage-eslint": "dist/cli/bin/eslint.js", "ankhorage-knip": "dist/cli/bin/knip.js", "ankhorage-prettier": "dist/cli/bin/prettier.js" } }, "sha512-ub8jve1q+7kQyftz0WcbBurbq1R6BURqXkziDj3NG4sQ+80vDWYFzE4hwjm3DbPJNycHdIZZZCKq+yoFyiwOPQ=="], @@ -2057,8 +2058,6 @@ "@ankhorage/ankh/@ankhorage/contracts": ["@ankhorage/contracts@2.1.0", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-QGG0atpGDe9+BUWzWvV83Kj309TBOK76yBRbWOoQli8g0LY8QLb7Z4UquD+qVhtWJTp3Y1jHUTEttQ0WjdhY/Q=="], - "@ankhorage/data-sources/@ankhorage/contracts": ["@ankhorage/contracts@1.19.2", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-HSkpXBsDgOFTq5tW6rYJNJn1cNzBxJCIm/JMypKaZcVy8rgvMK4PDBYKVyyEL4IwPHRyuWqVC9HKa0SosYeGXQ=="], - "@ankhorage/doctor/@ankhorage/contracts": ["@ankhorage/contracts@1.19.3", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-wMzlSaaJQeWyd5pQ2Eh/MDrDYGT2PHC2Of9KTTibJ+tBUpQDrRXMcBTklZIJY3vAma2FB/pHIRl2pzEj0weDbg=="], "@ankhorage/expo-runtime/@ankhorage/contracts": ["@ankhorage/contracts@2.1.0", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-QGG0atpGDe9+BUWzWvV83Kj309TBOK76yBRbWOoQli8g0LY8QLb7Z4UquD+qVhtWJTp3Y1jHUTEttQ0WjdhY/Q=="], @@ -2067,6 +2066,8 @@ "@ankhorage/runtime/@ankhorage/contracts": ["@ankhorage/contracts@2.1.0", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-QGG0atpGDe9+BUWzWvV83Kj309TBOK76yBRbWOoQli8g0LY8QLb7Z4UquD+qVhtWJTp3Y1jHUTEttQ0WjdhY/Q=="], + "@ankhorage/runtime/@ankhorage/data-sources": ["@ankhorage/data-sources@0.5.2", "", { "dependencies": { "@ankhorage/contracts": "^1.18.3" } }, "sha512-Z/miagAQSjYeDWH70LTRqn0e52+N58/dDWW70MLqTW1udmQxmUrJkjQ1BXocl1yAq8rcQBus+xvkJd9D40xOFw=="], + "@ankhorage/supabase-auth/@ankhorage/contracts": ["@ankhorage/contracts@3.0.0", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-IXBdPehcgJpDjl/0QXHunl7AsgkTAXoC89jBiQWE+tanp8SBdgVFxWMm9A1MtbU3Rr1UU6U81GjhASM2OhGVUw=="], "@ankhorage/supabase-vault/@ankhorage/contracts": ["@ankhorage/contracts@2.1.0", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-QGG0atpGDe9+BUWzWvV83Kj309TBOK76yBRbWOoQli8g0LY8QLb7Z4UquD+qVhtWJTp3Y1jHUTEttQ0WjdhY/Q=="], @@ -2393,8 +2394,6 @@ "@ankhorage/ankh/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], - "@ankhorage/data-sources/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], - "@ankhorage/doctor/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], "@ankhorage/expo-runtime/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], @@ -2403,6 +2402,8 @@ "@ankhorage/runtime/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], + "@ankhorage/runtime/@ankhorage/data-sources/@ankhorage/contracts": ["@ankhorage/contracts@1.19.2", "", { "dependencies": { "@ankhorage/color-theory": "^0.0.7" } }, "sha512-HSkpXBsDgOFTq5tW6rYJNJn1cNzBxJCIm/JMypKaZcVy8rgvMK4PDBYKVyyEL4IwPHRyuWqVC9HKa0SosYeGXQ=="], + "@ankhorage/supabase-auth/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], "@ankhorage/supabase-vault/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], @@ -2575,6 +2576,8 @@ "test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], + "@ankhorage/runtime/@ankhorage/data-sources/@ankhorage/contracts/@ankhorage/color-theory": ["@ankhorage/color-theory@0.0.7", "", { "dependencies": { "culori": "^4.0.2" } }, "sha512-fcdJZMmhfbvc9Yhz5vjTolmWYAaCsUaUOq4/P6YDZLHhXer8Mv8y0LaOhWgaXcwq48nrxILGUMOxOl48tRhTmA=="], + "@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], diff --git a/docs/external-api-authoring.md b/docs/external-api-authoring.md new file mode 100644 index 00000000..0fac978e --- /dev/null +++ b/docs/external-api-authoring.md @@ -0,0 +1,25 @@ +# External API authoring + +`/ankh/apis` connects existing services into the canonical manifest `dataSources` registry. Studio does not own OpenAPI parsing, GraphQL introspection, REST normalization, or endpoint execution; those capabilities come from `@ankhorage/data-sources`. + +## Trusted host boundary + +The browser submits only authoring intent and provider-neutral credential references. The local Studio host performs discovery and operation tests through a constrained HTTP transport with timeouts, response-size limits, redirect rejection, and cloud metadata target blocking. Trusted secret values are resolved only inside the host and are removed from browser-readable operation diagnostics. + +## Persistence + +Successful OpenAPI discovery, GraphQL introspection, and manual REST creation upsert the resulting `DataSourceConfig` directly into the editable Studio manifest. Reusing a normalized source ID updates that source. There is no secondary API catalog or unsaved API model. + +## Authoring flows + +- **Auto discovery** probes direct and conventional OpenAPI document locations before trying GraphQL introspection at the supplied URL. +- **OpenAPI** uses only the canonical discovery/import flow. +- **GraphQL** executes the canonical introspection request and normalizes discovered operations. +- **Manual REST** provides an explicit fallback when no schema document exists. +- **Operation testing** uses the canonical data-source test runner. Browser responses omit request headers and bodies and redact query values. + +Package-neutral source-ID normalization and registry upsert helpers are exported through `@ankhorage/studio/externalApiAuthoring`. + +## Validation boundary + +Changes to this flow must validate the package model and trusted host behavior, the Studio web workspace, the generated app web administration surface, and the published package contents. External provider availability is injected or mocked in deterministic tests; real credentials never belong in repository or browser test fixtures. diff --git a/package.json b/package.json index 6e97cbbe..944a0952 100644 --- a/package.json +++ b/package.json @@ -179,6 +179,10 @@ "./host": { "types": "./dist/host/index.d.ts", "import": "./dist/host/index.js" + }, + "./externalApiAuthoring": { + "types": "./dist/externalApiAuthoring.d.ts", + "import": "./dist/externalApiAuthoring.js" } }, "ankh": { @@ -194,7 +198,9 @@ "studio.secrets.list", "studio.secrets.create", "studio.secrets.replace", - "studio.secrets.remove" + "studio.secrets.remove", + "studio.data-sources.connect", + "studio.data-sources.test" ] }, "scripts": { @@ -234,6 +240,7 @@ "dependencies": { "@ankhorage/color-theory": "^0.0.8", "@ankhorage/contracts": "^4.0.1", + "@ankhorage/data-sources": "^0.6.0", "@ankhorage/expo-runtime": "^0.0.11", "@ankhorage/infra": "^1.1.0", "@ankhorage/orchestrator": "^0.2.4", diff --git a/paradox/badges/npm.svg b/paradox/badges/npm.svg index e8360975..33630363 100644 --- a/paradox/badges/npm.svg +++ b/paradox/badges/npm.svg @@ -1,7 +1,7 @@ - -npm: v0.11.11 + +npm: v0.11.12 npm -v0.11.11 +v0.11.12 diff --git a/paradox/diagrams/architecture-overview.mmd b/paradox/diagrams/architecture-overview.mmd index 29c730eb..1ef9f78d 100644 --- a/paradox/diagrams/architecture-overview.mmd +++ b/paradox/diagrams/architecture-overview.mmd @@ -152,6 +152,17 @@ graph TD package__ankhorage_studio -.-> module_src_dnd_primitives_ts module_src_dnd_state_ts["src/dnd/state.ts"] package__ankhorage_studio -.-> module_src_dnd_state_ts + module_src_externalApiApi_ts["src/externalApiApi.ts"] + package__ankhorage_studio -.-> module_src_externalApiApi_ts + module_src_externalApiApi_ts --> module_src_core_constants_ts + module_src_externalApiApi_ts --> module_src_externalApiAuthoringContracts_ts + module_src_externalApiAuthoring_public_test_ts["src/externalApiAuthoring.public.test.ts"] + package__ankhorage_studio -.-> module_src_externalApiAuthoring_public_test_ts + module_src_externalApiAuthoring_public_test_ts --> module_src_externalApiAuthoring_ts + module_src_externalApiAuthoring_ts["src/externalApiAuthoring.ts"] + package__ankhorage_studio -.-> module_src_externalApiAuthoring_ts + module_src_externalApiAuthoringContracts_ts["src/externalApiAuthoringContracts.ts"] + package__ankhorage_studio -.-> module_src_externalApiAuthoringContracts_ts module_src_hooks_useProjects_ts["src/hooks/useProjects.ts"] package__ankhorage_studio -.-> module_src_hooks_useProjects_ts module_src_hooks_useProjects_ts --> module_src_contractGuards_ts @@ -186,6 +197,38 @@ graph TD module_src_host_createStudioHost_ts --> module_src_host_orchestrator_infraRuntime_ts module_src_host_createStudioHost_ts --> module_src_host_orchestrator_moduleManager_ts module_src_host_createStudioHost_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_dataSources_createProjectEndpointCredentialResolver_ts["src/host/dataSources/createProjectEndpointCredentialResolver.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_createProjectEndpointCredentialResolver_ts + module_src_host_dataSources_createProjectEndpointCredentialResolver_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_dataSources_createTrustedEndpointTestFetch_ts["src/host/dataSources/createTrustedEndpointTestFetch.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_createTrustedEndpointTestFetch_ts + module_src_host_dataSources_createTrustedEndpointTestFetch_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_createTrustedExternalApiFetch_ts["src/host/dataSources/createTrustedExternalApiFetch.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_createTrustedExternalApiFetch_ts + module_src_host_dataSources_createTrustedExternalApiFetch_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_requestTrustedExternalApi_test_ts["src/host/dataSources/requestTrustedExternalApi.test.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_requestTrustedExternalApi_test_ts + module_src_host_dataSources_requestTrustedExternalApi_test_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_requestTrustedExternalApi_ts["src/host/dataSources/requestTrustedExternalApi.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts["src/host/dataSources/sanitizeExternalApiOperationTestResult.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts + module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_dataSources_studioExternalApiService_test_ts["src/host/dataSources/studioExternalApiService.test.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_studioExternalApiService_test_ts + module_src_host_dataSources_studioExternalApiService_test_ts --> module_src_host_dataSources_studioExternalApiService_ts + module_src_host_dataSources_studioExternalApiService_test_ts --> module_src_index_ts + module_src_host_dataSources_studioExternalApiService_ts["src/host/dataSources/studioExternalApiService.ts"] + package__ankhorage_studio -.-> module_src_host_dataSources_studioExternalApiService_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createProjectEndpointCredentialResolver_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createTrustedEndpointTestFetch_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createTrustedExternalApiFetch_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_normalizeExternalApiSourceId_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_upsertExternalApiDataSource_ts module_src_host_generatedAdminExpoWeb_smoke_test_ts["src/host/generatedAdminExpoWeb.smoke.test.ts"] package__ankhorage_studio -.-> module_src_host_generatedAdminExpoWeb_smoke_test_ts module_src_host_generatedAdminExpoWeb_smoke_test_ts --> module_src_host_installedPackageProvenance_ts @@ -220,6 +263,12 @@ graph TD module_src_host_http_authRoutes_ts --> module_src_host_auth_projectAuthHealthService_ts module_src_host_http_authRoutes_ts --> module_src_host_orchestrator_projectManager_ts module_src_host_http_authRoutes_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_http_dataSourceRoutes_ts["src/host/http/dataSourceRoutes.ts"] + package__ankhorage_studio -.-> module_src_host_http_dataSourceRoutes_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_dataSources_studioExternalApiService_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_secrets_projectSecretService_ts module_src_host_http_secretRoutes_ts["src/host/http/secretRoutes.ts"] package__ankhorage_studio -.-> module_src_host_http_secretRoutes_ts module_src_host_http_secretRoutes_ts --> module_src_host_orchestrator_projectManager_ts @@ -242,6 +291,7 @@ graph TD module_src_host_http_serverWithSecrets_ts["src/host/http/serverWithSecrets.ts"] package__ankhorage_studio -.-> module_src_host_http_serverWithSecrets_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_authRoutes_ts + module_src_host_http_serverWithSecrets_ts --> module_src_host_http_dataSourceRoutes_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_secretRoutes_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_server_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_orchestrator_moduleManager_ts @@ -586,6 +636,8 @@ graph TD module_src_manifestSync_ts["src/manifestSync.ts"] package__ankhorage_studio -.-> module_src_manifestSync_ts module_src_manifestSync_ts --> module_src_index_ts + module_src_normalizeExternalApiSourceId_ts["src/normalizeExternalApiSourceId.ts"] + package__ankhorage_studio -.-> module_src_normalizeExternalApiSourceId_ts module_src_projectAuthApi_test_ts["src/projectAuthApi.test.ts"] package__ankhorage_studio -.-> module_src_projectAuthApi_test_ts module_src_projectAuthApi_test_ts --> module_src_projectAuthApi_ts @@ -757,8 +809,6 @@ graph TD package__ankhorage_studio -.-> module_src_templateCatalogContracts_ts module_src_ui_admin_adminPagePrimitives_tsx["src/ui/admin/adminPagePrimitives.tsx"] package__ankhorage_studio -.-> module_src_ui_admin_adminPagePrimitives_tsx - module_src_ui_admin_adminPageUtils_ts["src/ui/admin/adminPageUtils.ts"] - package__ankhorage_studio -.-> module_src_ui_admin_adminPageUtils_ts module_src_ui_admin_AnkhAdminPage_test_ts["src/ui/admin/AnkhAdminPage.test.ts"] package__ankhorage_studio -.-> module_src_ui_admin_AnkhAdminPage_test_ts module_src_ui_admin_AnkhAdminPage_tsx["src/ui/admin/AnkhAdminPage.tsx"] @@ -806,9 +856,11 @@ graph TD package__ankhorage_studio -.-> module_src_ui_admin_pages_adminAuthSessionModel_ts module_src_ui_admin_pages_adminAuthSessionModel_ts --> module_src_authSettings_ts module_src_ui_admin_pages_adminAuthSessionModel_ts --> module_src_ui_admin_pages_adminAuthCredentialFlow_ts + module_src_ui_admin_pages_adminDataSourceOperations_test_ts["src/ui/admin/pages/adminDataSourceOperations.test.ts"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_adminDataSourceOperations_test_ts + module_src_ui_admin_pages_adminDataSourceOperations_test_ts --> module_src_ui_admin_pages_adminDataSourceOperations_ts module_src_ui_admin_pages_adminDataSourceOperations_ts["src/ui/admin/pages/adminDataSourceOperations.ts"] package__ankhorage_studio -.-> module_src_ui_admin_pages_adminDataSourceOperations_ts - module_src_ui_admin_pages_adminDataSourceOperations_ts --> module_src_ui_admin_adminPageUtils_ts module_src_ui_admin_pages_adminThemeHarmony_ts["src/ui/admin/pages/adminThemeHarmony.ts"] package__ankhorage_studio -.-> module_src_ui_admin_pages_adminThemeHarmony_ts module_src_ui_admin_pages_adminThemeModel_test_ts["src/ui/admin/pages/adminThemeModel.test.ts"] @@ -825,8 +877,10 @@ graph TD module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_core_StudioContext_ts module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_index_ts module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_adminPagePrimitives_tsx - module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_adminPageUtils_ts - module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_adminDataSourceOperations_ts + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_DataSourceCatalogCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_DataSourceOperationsCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_ExternalApiConnectCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_ManualRestSourceCard_tsx module_src_ui_admin_pages_AuthAdminPage_test_ts["src/ui/admin/pages/AuthAdminPage.test.ts"] package__ankhorage_studio -.-> module_src_ui_admin_pages_AuthAdminPage_test_ts module_src_ui_admin_pages_AuthAdminPage_tsx["src/ui/admin/pages/AuthAdminPage.tsx"] @@ -842,6 +896,31 @@ graph TD module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthCredentialFlow_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthHealthFlow_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthSessionModel_ts + module_src_ui_admin_pages_DataSourceCatalogCard_tsx["src/ui/admin/pages/DataSourceCatalogCard.tsx"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_DataSourceCatalogCard_tsx + module_src_ui_admin_pages_DataSourceCatalogCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_DataSourceOperationsCard_tsx["src/ui/admin/pages/DataSourceOperationsCard.tsx"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_DataSourceOperationsCard_tsx + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_ui_admin_pages_adminDataSourceOperations_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx["src/ui/admin/pages/ExternalApiAdminPrimitives.tsx"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx["src/ui/admin/pages/ExternalApiConnectCard.tsx"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_ExternalApiConnectCard_tsx + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_ManualRestSourceCard_tsx["src/ui/admin/pages/ManualRestSourceCard.tsx"] + package__ankhorage_studio -.-> module_src_ui_admin_pages_ManualRestSourceCard_tsx + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx module_src_ui_admin_pages_OverviewAdminPage_tsx["src/ui/admin/pages/OverviewAdminPage.tsx"] package__ankhorage_studio -.-> module_src_ui_admin_pages_OverviewAdminPage_tsx module_src_ui_admin_pages_OverviewAdminPage_tsx --> module_src_core_StudioContext_ts @@ -886,6 +965,8 @@ graph TD module_src_ui_useStudioAppBarAugmentation_ts --> module_src_studioAdminRouteModel_ts module_src_ui_useStudioAppBarAugmentation_ts --> module_src_studioSelectionModel_ts module_src_ui_useStudioAppBarAugmentation_ts --> module_src_ui_studioAppBarModel_ts + module_src_upsertExternalApiDataSource_ts["src/upsertExternalApiDataSource.ts"] + package__ankhorage_studio -.-> module_src_upsertExternalApiDataSource_ts module_src_utils_treeUtils_ts["src/utils/treeUtils.ts"] package__ankhorage_studio -.-> module_src_utils_treeUtils_ts module_src_utils_treeUtils_ts --> module_src_index_ts diff --git a/paradox/diagrams/export-graph.mmd b/paradox/diagrams/export-graph.mmd index 0610354a..c07c502c 100644 --- a/paradox/diagrams/export-graph.mmd +++ b/paradox/diagrams/export-graph.mmd @@ -40,6 +40,10 @@ graph LR module_src_dnd_index_ts["src/dnd/index.ts"] module_src_dnd_primitives_ts["src/dnd/primitives.ts"] module_src_dnd_state_ts["src/dnd/state.ts"] + module_src_externalApiApi_ts["src/externalApiApi.ts"] + module_src_externalApiAuthoring_public_test_ts["src/externalApiAuthoring.public.test.ts"] + module_src_externalApiAuthoring_ts["src/externalApiAuthoring.ts"] + module_src_externalApiAuthoringContracts_ts["src/externalApiAuthoringContracts.ts"] module_src_hooks_useProjects_ts["src/hooks/useProjects.ts"] module_src_hooks_useTemplateCatalog_ts["src/hooks/useTemplateCatalog.ts"] module_src_hooks_useWorkspaceActions_ts["src/hooks/useWorkspaceActions.ts"] @@ -47,6 +51,14 @@ graph LR module_src_host_auth_projectAuthRuntimeDiagnostics_test_ts["src/host/auth/projectAuthRuntimeDiagnostics.test.ts"] module_src_host_auth_projectAuthRuntimeDiagnostics_ts["src/host/auth/projectAuthRuntimeDiagnostics.ts"] module_src_host_createStudioHost_ts["src/host/createStudioHost.ts"] + module_src_host_dataSources_createProjectEndpointCredentialResolver_ts["src/host/dataSources/createProjectEndpointCredentialResolver.ts"] + module_src_host_dataSources_createTrustedEndpointTestFetch_ts["src/host/dataSources/createTrustedEndpointTestFetch.ts"] + module_src_host_dataSources_createTrustedExternalApiFetch_ts["src/host/dataSources/createTrustedExternalApiFetch.ts"] + module_src_host_dataSources_requestTrustedExternalApi_test_ts["src/host/dataSources/requestTrustedExternalApi.test.ts"] + module_src_host_dataSources_requestTrustedExternalApi_ts["src/host/dataSources/requestTrustedExternalApi.ts"] + module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts["src/host/dataSources/sanitizeExternalApiOperationTestResult.ts"] + module_src_host_dataSources_studioExternalApiService_test_ts["src/host/dataSources/studioExternalApiService.test.ts"] + module_src_host_dataSources_studioExternalApiService_ts["src/host/dataSources/studioExternalApiService.ts"] module_src_host_generatedAdminExpoWeb_smoke_test_ts["src/host/generatedAdminExpoWeb.smoke.test.ts"] module_src_host_generatedAuthAdapterRuntime_smoke_test_ts["src/host/generatedAuthAdapterRuntime.smoke.test.ts"] module_src_host_generatedOAuthLifecycle_e2e_test_ts["src/host/generatedOAuthLifecycle.e2e.test.ts"] @@ -54,6 +66,7 @@ graph LR module_src_host_hostOwnership_test_ts["src/host/hostOwnership.test.ts"] module_src_host_http_authRoutes_test_ts["src/host/http/authRoutes.test.ts"] module_src_host_http_authRoutes_ts["src/host/http/authRoutes.ts"] + module_src_host_http_dataSourceRoutes_ts["src/host/http/dataSourceRoutes.ts"] module_src_host_http_secretRoutes_ts["src/host/http/secretRoutes.ts"] module_src_host_http_security_ts["src/host/http/security.ts"] module_src_host_http_server_ts["src/host/http/server.ts"] @@ -158,6 +171,7 @@ graph LR module_src_manifestState_ts["src/manifestState.ts"] module_src_manifestSync_test_ts["src/manifestSync.test.ts"] module_src_manifestSync_ts["src/manifestSync.ts"] + module_src_normalizeExternalApiSourceId_ts["src/normalizeExternalApiSourceId.ts"] module_src_projectAuthApi_test_ts["src/projectAuthApi.test.ts"] module_src_projectAuthApi_ts["src/projectAuthApi.ts"] module_src_projectAuthHealth_test_ts["src/projectAuthHealth.test.ts"] @@ -220,7 +234,6 @@ graph LR module_src_studioSelectionModel_ts["src/studioSelectionModel.ts"] module_src_templateCatalogContracts_ts["src/templateCatalogContracts.ts"] module_src_ui_admin_adminPagePrimitives_tsx["src/ui/admin/adminPagePrimitives.tsx"] - module_src_ui_admin_adminPageUtils_ts["src/ui/admin/adminPageUtils.ts"] module_src_ui_admin_AnkhAdminPage_test_ts["src/ui/admin/AnkhAdminPage.test.ts"] module_src_ui_admin_AnkhAdminPage_tsx["src/ui/admin/AnkhAdminPage.tsx"] module_src_ui_admin_AnkhAdminShell_test_ts["src/ui/admin/AnkhAdminShell.test.ts"] @@ -232,6 +245,7 @@ graph LR module_src_ui_admin_pages_adminAuthHealthFlow_ts["src/ui/admin/pages/adminAuthHealthFlow.ts"] module_src_ui_admin_pages_adminAuthSessionModel_test_ts["src/ui/admin/pages/adminAuthSessionModel.test.ts"] module_src_ui_admin_pages_adminAuthSessionModel_ts["src/ui/admin/pages/adminAuthSessionModel.ts"] + module_src_ui_admin_pages_adminDataSourceOperations_test_ts["src/ui/admin/pages/adminDataSourceOperations.test.ts"] module_src_ui_admin_pages_adminDataSourceOperations_ts["src/ui/admin/pages/adminDataSourceOperations.ts"] module_src_ui_admin_pages_adminThemeHarmony_ts["src/ui/admin/pages/adminThemeHarmony.ts"] module_src_ui_admin_pages_adminThemeModel_test_ts["src/ui/admin/pages/adminThemeModel.test.ts"] @@ -240,6 +254,11 @@ graph LR module_src_ui_admin_pages_ApisAdminPage_tsx["src/ui/admin/pages/ApisAdminPage.tsx"] module_src_ui_admin_pages_AuthAdminPage_test_ts["src/ui/admin/pages/AuthAdminPage.test.ts"] module_src_ui_admin_pages_AuthAdminPage_tsx["src/ui/admin/pages/AuthAdminPage.tsx"] + module_src_ui_admin_pages_DataSourceCatalogCard_tsx["src/ui/admin/pages/DataSourceCatalogCard.tsx"] + module_src_ui_admin_pages_DataSourceOperationsCard_tsx["src/ui/admin/pages/DataSourceOperationsCard.tsx"] + module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx["src/ui/admin/pages/ExternalApiAdminPrimitives.tsx"] + module_src_ui_admin_pages_ExternalApiConnectCard_tsx["src/ui/admin/pages/ExternalApiConnectCard.tsx"] + module_src_ui_admin_pages_ManualRestSourceCard_tsx["src/ui/admin/pages/ManualRestSourceCard.tsx"] module_src_ui_admin_pages_OverviewAdminPage_tsx["src/ui/admin/pages/OverviewAdminPage.tsx"] module_src_ui_admin_pages_PropertiesAdminPage_test_ts["src/ui/admin/pages/PropertiesAdminPage.test.ts"] module_src_ui_admin_pages_PropertiesAdminPage_tsx["src/ui/admin/pages/PropertiesAdminPage.tsx"] @@ -251,6 +270,7 @@ graph LR module_src_ui_studioAppBarModel_ts["src/ui/studioAppBarModel.ts"] module_src_ui_useStudioAppBarAugmentation_test_ts["src/ui/useStudioAppBarAugmentation.test.ts"] module_src_ui_useStudioAppBarAugmentation_ts["src/ui/useStudioAppBarAugmentation.ts"] + module_src_upsertExternalApiDataSource_ts["src/upsertExternalApiDataSource.ts"] module_src_utils_treeUtils_ts["src/utils/treeUtils.ts"] module_src_workspacePlatform_test_ts["src/workspacePlatform.test.ts"] module_src_workspacePlatform_ts["src/workspacePlatform.ts"] diff --git a/paradox/diagrams/module-relationships.mmd b/paradox/diagrams/module-relationships.mmd index ef7e0531..f3660fc5 100644 --- a/paradox/diagrams/module-relationships.mmd +++ b/paradox/diagrams/module-relationships.mmd @@ -40,6 +40,10 @@ graph LR module_src_dnd_index_ts["src/dnd/index.ts"] module_src_dnd_primitives_ts["src/dnd/primitives.ts"] module_src_dnd_state_ts["src/dnd/state.ts"] + module_src_externalApiApi_ts["src/externalApiApi.ts"] + module_src_externalApiAuthoring_public_test_ts["src/externalApiAuthoring.public.test.ts"] + module_src_externalApiAuthoring_ts["src/externalApiAuthoring.ts"] + module_src_externalApiAuthoringContracts_ts["src/externalApiAuthoringContracts.ts"] module_src_hooks_useProjects_ts["src/hooks/useProjects.ts"] module_src_hooks_useTemplateCatalog_ts["src/hooks/useTemplateCatalog.ts"] module_src_hooks_useWorkspaceActions_ts["src/hooks/useWorkspaceActions.ts"] @@ -47,6 +51,14 @@ graph LR module_src_host_auth_projectAuthRuntimeDiagnostics_test_ts["src/host/auth/projectAuthRuntimeDiagnostics.test.ts"] module_src_host_auth_projectAuthRuntimeDiagnostics_ts["src/host/auth/projectAuthRuntimeDiagnostics.ts"] module_src_host_createStudioHost_ts["src/host/createStudioHost.ts"] + module_src_host_dataSources_createProjectEndpointCredentialResolver_ts["src/host/dataSources/createProjectEndpointCredentialResolver.ts"] + module_src_host_dataSources_createTrustedEndpointTestFetch_ts["src/host/dataSources/createTrustedEndpointTestFetch.ts"] + module_src_host_dataSources_createTrustedExternalApiFetch_ts["src/host/dataSources/createTrustedExternalApiFetch.ts"] + module_src_host_dataSources_requestTrustedExternalApi_test_ts["src/host/dataSources/requestTrustedExternalApi.test.ts"] + module_src_host_dataSources_requestTrustedExternalApi_ts["src/host/dataSources/requestTrustedExternalApi.ts"] + module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts["src/host/dataSources/sanitizeExternalApiOperationTestResult.ts"] + module_src_host_dataSources_studioExternalApiService_test_ts["src/host/dataSources/studioExternalApiService.test.ts"] + module_src_host_dataSources_studioExternalApiService_ts["src/host/dataSources/studioExternalApiService.ts"] module_src_host_generatedAdminExpoWeb_smoke_test_ts["src/host/generatedAdminExpoWeb.smoke.test.ts"] module_src_host_generatedAuthAdapterRuntime_smoke_test_ts["src/host/generatedAuthAdapterRuntime.smoke.test.ts"] module_src_host_generatedOAuthLifecycle_e2e_test_ts["src/host/generatedOAuthLifecycle.e2e.test.ts"] @@ -54,6 +66,7 @@ graph LR module_src_host_hostOwnership_test_ts["src/host/hostOwnership.test.ts"] module_src_host_http_authRoutes_test_ts["src/host/http/authRoutes.test.ts"] module_src_host_http_authRoutes_ts["src/host/http/authRoutes.ts"] + module_src_host_http_dataSourceRoutes_ts["src/host/http/dataSourceRoutes.ts"] module_src_host_http_secretRoutes_ts["src/host/http/secretRoutes.ts"] module_src_host_http_security_ts["src/host/http/security.ts"] module_src_host_http_server_ts["src/host/http/server.ts"] @@ -158,6 +171,7 @@ graph LR module_src_manifestState_ts["src/manifestState.ts"] module_src_manifestSync_test_ts["src/manifestSync.test.ts"] module_src_manifestSync_ts["src/manifestSync.ts"] + module_src_normalizeExternalApiSourceId_ts["src/normalizeExternalApiSourceId.ts"] module_src_projectAuthApi_test_ts["src/projectAuthApi.test.ts"] module_src_projectAuthApi_ts["src/projectAuthApi.ts"] module_src_projectAuthHealth_test_ts["src/projectAuthHealth.test.ts"] @@ -220,7 +234,6 @@ graph LR module_src_studioSelectionModel_ts["src/studioSelectionModel.ts"] module_src_templateCatalogContracts_ts["src/templateCatalogContracts.ts"] module_src_ui_admin_adminPagePrimitives_tsx["src/ui/admin/adminPagePrimitives.tsx"] - module_src_ui_admin_adminPageUtils_ts["src/ui/admin/adminPageUtils.ts"] module_src_ui_admin_AnkhAdminPage_test_ts["src/ui/admin/AnkhAdminPage.test.ts"] module_src_ui_admin_AnkhAdminPage_tsx["src/ui/admin/AnkhAdminPage.tsx"] module_src_ui_admin_AnkhAdminShell_test_ts["src/ui/admin/AnkhAdminShell.test.ts"] @@ -232,6 +245,7 @@ graph LR module_src_ui_admin_pages_adminAuthHealthFlow_ts["src/ui/admin/pages/adminAuthHealthFlow.ts"] module_src_ui_admin_pages_adminAuthSessionModel_test_ts["src/ui/admin/pages/adminAuthSessionModel.test.ts"] module_src_ui_admin_pages_adminAuthSessionModel_ts["src/ui/admin/pages/adminAuthSessionModel.ts"] + module_src_ui_admin_pages_adminDataSourceOperations_test_ts["src/ui/admin/pages/adminDataSourceOperations.test.ts"] module_src_ui_admin_pages_adminDataSourceOperations_ts["src/ui/admin/pages/adminDataSourceOperations.ts"] module_src_ui_admin_pages_adminThemeHarmony_ts["src/ui/admin/pages/adminThemeHarmony.ts"] module_src_ui_admin_pages_adminThemeModel_test_ts["src/ui/admin/pages/adminThemeModel.test.ts"] @@ -240,6 +254,11 @@ graph LR module_src_ui_admin_pages_ApisAdminPage_tsx["src/ui/admin/pages/ApisAdminPage.tsx"] module_src_ui_admin_pages_AuthAdminPage_test_ts["src/ui/admin/pages/AuthAdminPage.test.ts"] module_src_ui_admin_pages_AuthAdminPage_tsx["src/ui/admin/pages/AuthAdminPage.tsx"] + module_src_ui_admin_pages_DataSourceCatalogCard_tsx["src/ui/admin/pages/DataSourceCatalogCard.tsx"] + module_src_ui_admin_pages_DataSourceOperationsCard_tsx["src/ui/admin/pages/DataSourceOperationsCard.tsx"] + module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx["src/ui/admin/pages/ExternalApiAdminPrimitives.tsx"] + module_src_ui_admin_pages_ExternalApiConnectCard_tsx["src/ui/admin/pages/ExternalApiConnectCard.tsx"] + module_src_ui_admin_pages_ManualRestSourceCard_tsx["src/ui/admin/pages/ManualRestSourceCard.tsx"] module_src_ui_admin_pages_OverviewAdminPage_tsx["src/ui/admin/pages/OverviewAdminPage.tsx"] module_src_ui_admin_pages_PropertiesAdminPage_test_ts["src/ui/admin/pages/PropertiesAdminPage.test.ts"] module_src_ui_admin_pages_PropertiesAdminPage_tsx["src/ui/admin/pages/PropertiesAdminPage.tsx"] @@ -251,6 +270,7 @@ graph LR module_src_ui_studioAppBarModel_ts["src/ui/studioAppBarModel.ts"] module_src_ui_useStudioAppBarAugmentation_test_ts["src/ui/useStudioAppBarAugmentation.test.ts"] module_src_ui_useStudioAppBarAugmentation_ts["src/ui/useStudioAppBarAugmentation.ts"] + module_src_upsertExternalApiDataSource_ts["src/upsertExternalApiDataSource.ts"] module_src_utils_treeUtils_ts["src/utils/treeUtils.ts"] module_src_workspacePlatform_test_ts["src/workspacePlatform.test.ts"] module_src_workspacePlatform_ts["src/workspacePlatform.ts"] @@ -323,6 +343,9 @@ graph LR module_src_core_StudioProvider_ts --> module_src_routeUtils_ts module_src_core_StudioProvider_ts --> module_src_studioSelectionModel_ts module_src_core_StudioProvider_ts --> module_src_ui_admin_AuthAdminSession_tsx + module_src_externalApiApi_ts --> module_src_core_constants_ts + module_src_externalApiApi_ts --> module_src_externalApiAuthoringContracts_ts + module_src_externalApiAuthoring_public_test_ts --> module_src_externalApiAuthoring_ts module_src_hooks_useProjects_ts --> module_src_contractGuards_ts module_src_hooks_useProjects_ts --> module_src_core_constants_ts module_src_hooks_useProjects_ts --> module_src_projectWorkspaceContracts_ts @@ -343,6 +366,22 @@ graph LR module_src_host_createStudioHost_ts --> module_src_host_orchestrator_infraRuntime_ts module_src_host_createStudioHost_ts --> module_src_host_orchestrator_moduleManager_ts module_src_host_createStudioHost_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_dataSources_createProjectEndpointCredentialResolver_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_dataSources_createTrustedEndpointTestFetch_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_createTrustedExternalApiFetch_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_requestTrustedExternalApi_test_ts --> module_src_host_dataSources_requestTrustedExternalApi_ts + module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_dataSources_studioExternalApiService_test_ts --> module_src_host_dataSources_studioExternalApiService_ts + module_src_host_dataSources_studioExternalApiService_test_ts --> module_src_index_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createProjectEndpointCredentialResolver_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createTrustedEndpointTestFetch_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_createTrustedExternalApiFetch_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_dataSources_sanitizeExternalApiOperationTestResult_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_normalizeExternalApiSourceId_ts + module_src_host_dataSources_studioExternalApiService_ts --> module_src_upsertExternalApiDataSource_ts module_src_host_generatedAdminExpoWeb_smoke_test_ts --> module_src_host_installedPackageProvenance_ts module_src_host_generatedAdminExpoWeb_smoke_test_ts --> module_src_host_orchestrator_moduleManager_ts module_src_host_generatedAdminExpoWeb_smoke_test_ts --> module_src_host_orchestrator_projectManager_ts @@ -363,6 +402,10 @@ graph LR module_src_host_http_authRoutes_ts --> module_src_host_auth_projectAuthHealthService_ts module_src_host_http_authRoutes_ts --> module_src_host_orchestrator_projectManager_ts module_src_host_http_authRoutes_ts --> module_src_host_secrets_projectSecretService_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_externalApiAuthoringContracts_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_dataSources_studioExternalApiService_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_orchestrator_projectManager_ts + module_src_host_http_dataSourceRoutes_ts --> module_src_host_secrets_projectSecretService_ts module_src_host_http_secretRoutes_ts --> module_src_host_orchestrator_projectManager_ts module_src_host_http_secretRoutes_ts --> module_src_host_secrets_projectSecretService_ts module_src_host_http_server_ts --> module_src_contractGuards_ts @@ -377,6 +420,7 @@ graph LR module_src_host_http_server_ts --> module_src_host_utils_workspaceRoot_ts module_src_host_http_server_ts --> module_src_projectIdentity_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_authRoutes_ts + module_src_host_http_serverWithSecrets_ts --> module_src_host_http_dataSourceRoutes_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_secretRoutes_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_http_server_ts module_src_host_http_serverWithSecrets_ts --> module_src_host_orchestrator_moduleManager_ts @@ -594,15 +638,17 @@ graph LR module_src_ui_admin_pages_adminAuthSessionModel_test_ts --> module_src_ui_admin_pages_adminAuthSessionModel_ts module_src_ui_admin_pages_adminAuthSessionModel_ts --> module_src_authSettings_ts module_src_ui_admin_pages_adminAuthSessionModel_ts --> module_src_ui_admin_pages_adminAuthCredentialFlow_ts - module_src_ui_admin_pages_adminDataSourceOperations_ts --> module_src_ui_admin_adminPageUtils_ts + module_src_ui_admin_pages_adminDataSourceOperations_test_ts --> module_src_ui_admin_pages_adminDataSourceOperations_ts module_src_ui_admin_pages_adminThemeModel_test_ts --> module_src_ui_admin_pages_adminThemeModel_ts module_src_ui_admin_pages_adminThemeModel_ts --> module_src_index_ts module_src_ui_admin_pages_ApisAdminPage_test_ts --> module_src_ui_admin_pages_adminDataSourceOperations_ts module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_core_StudioContext_ts module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_index_ts module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_adminPagePrimitives_tsx - module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_adminPageUtils_ts - module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_adminDataSourceOperations_ts + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_DataSourceCatalogCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_DataSourceOperationsCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_ExternalApiConnectCard_tsx + module_src_ui_admin_pages_ApisAdminPage_tsx --> module_src_ui_admin_pages_ManualRestSourceCard_tsx module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_authSettings_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_core_StudioContext_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_index_ts @@ -614,6 +660,21 @@ graph LR module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthCredentialFlow_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthHealthFlow_ts module_src_ui_admin_pages_AuthAdminPage_tsx --> module_src_ui_admin_pages_adminAuthSessionModel_ts + module_src_ui_admin_pages_DataSourceCatalogCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_ui_admin_pages_adminDataSourceOperations_ts + module_src_ui_admin_pages_DataSourceOperationsCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ExternalApiConnectCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_core_StudioContext_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_externalApiApi_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_externalApiAuthoringContracts_ts + module_src_ui_admin_pages_ManualRestSourceCard_tsx --> module_src_ui_admin_pages_ExternalApiAdminPrimitives_tsx module_src_ui_admin_pages_OverviewAdminPage_tsx --> module_src_core_StudioContext_ts module_src_ui_admin_pages_OverviewAdminPage_tsx --> module_src_ui_admin_adminPagePrimitives_tsx module_src_ui_admin_pages_PropertiesAdminPage_tsx --> module_src_core_StudioContext_ts diff --git a/paradox/index.html b/paradox/index.html index 1191cb22..a42e07ef 100644 --- a/paradox/index.html +++ b/paradox/index.html @@ -399,6 +399,12 @@

@ankhorage/studio

1 function +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • -
  • - -
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • + + + {result ? : null} + + ); + })} + + )} + + ); +} + +function OperationResult({ result }: { readonly result: ExternalApiOperationTestResult }) { + return ( + + {result.request ? ( + + {result.request.dryRun ? 'Prepared' : 'Sent'} {result.request.method} {result.request.url} + + ) : null} + {result.response ? ( + + HTTP {result.response.status} · {result.response.ok ? 'success' : 'failure'} + + ) : null} + {result.ok && result.data !== undefined ? ( + + {formatData(result.data)} + + ) : null} + + + ); +} + +function operationKey(row: DataSourceOperationRow): string { + return `${row.sourceId}:${row.endpointId}:${row.operationId}`; +} + +function formatData(value: unknown): string { + const serialized = JSON.stringify(value, null, 2); + return serialized.length > 1_500 ? `${serialized.slice(0, 1_500)}…` : serialized; +} diff --git a/src/ui/admin/pages/ExternalApiAdminPrimitives.tsx b/src/ui/admin/pages/ExternalApiAdminPrimitives.tsx new file mode 100644 index 00000000..7e9c619c --- /dev/null +++ b/src/ui/admin/pages/ExternalApiAdminPrimitives.tsx @@ -0,0 +1,84 @@ +import type { DataSourceDiagnostic } from '@ankhorage/contracts/data'; +import { Text } from '@ankhorage/zora'; +import type { ReactNode } from 'react'; +import { StyleSheet, View } from 'react-native'; + +import type { ExternalApiDiscoveryAttempt } from '../../../externalApiAuthoringContracts'; + +export function ExternalApiField({ + label, + children, +}: { + readonly label: string; + readonly children: ReactNode; +}) { + return ( + + + {label} + + {children} + + ); +} + +export function ExternalApiDiagnosticList({ + diagnostics, + attempts = [], +}: { + readonly diagnostics: readonly DataSourceDiagnostic[]; + readonly attempts?: readonly ExternalApiDiscoveryAttempt[]; +}) { + if (diagnostics.length === 0 && attempts.length === 0) return null; + return ( + + {attempts.map((attempt) => ( + + {attempt.outcome}: {attempt.url} + {attempt.status === undefined ? '' : ` (HTTP ${attempt.status})`} + + ))} + {diagnostics.map((diagnostic, index) => ( + + {diagnostic.message} + + ))} + + ); +} + +export const externalApiAdminStyles = StyleSheet.create({ + actions: { + alignItems: 'center', + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }, + columns: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + }, + field: { + flexGrow: 1, + gap: 6, + minWidth: 220, + }, + feedback: { + gap: 4, + }, + operation: { + gap: 8, + }, + row: { + gap: 4, + paddingVertical: 8, + }, + stack: { + gap: 12, + }, +}); diff --git a/src/ui/admin/pages/ExternalApiConnectCard.tsx b/src/ui/admin/pages/ExternalApiConnectCard.tsx new file mode 100644 index 00000000..9c88203f --- /dev/null +++ b/src/ui/admin/pages/ExternalApiConnectCard.tsx @@ -0,0 +1,128 @@ +import { Button, Card, Input, Select, Text } from '@ankhorage/zora'; +import { useCallback, useState } from 'react'; +import { View } from 'react-native'; + +import { connectExternalApi } from '../../../externalApiApi'; +import type { + ExternalApiConnectResult, + ExternalApiProtocol, +} from '../../../externalApiAuthoringContracts'; +import { useStudio } from '../../../core/StudioContext'; +import { + ExternalApiDiagnosticList, + ExternalApiField, + externalApiAdminStyles, +} from './ExternalApiAdminPrimitives'; + +const PROTOCOL_OPTIONS = [ + { value: 'auto', label: 'Auto: OpenAPI, then GraphQL' }, + { value: 'openapi', label: 'OpenAPI' }, + { value: 'graphql', label: 'GraphQL introspection' }, +] as const; + +export function ExternalApiConnectCard() { + const studio = useStudio(); + const [sourceId, setSourceId] = useState(''); + const [url, setUrl] = useState(''); + const [name, setName] = useState(''); + const [protocol, setProtocol] = useState('auto'); + const [credentialId, setCredentialId] = useState(''); + const [credentialKind, setCredentialKind] = useState('bearer'); + const [credentialScope, setCredentialScope] = useState(''); + const [result, setResult] = useState(null); + const [message, setMessage] = useState(null); + const [busy, setBusy] = useState(false); + + const connect = useCallback(async () => { + setBusy(true); + setMessage(null); + try { + const next = await connectExternalApi(studio.projectId, { + sourceId, + url, + protocol, + name: name.trim() || undefined, + credential: credentialId.trim() + ? { + id: credentialId.trim(), + kind: credentialKind.trim() || 'bearer', + scope: credentialScope.trim() || undefined, + } + : undefined, + }); + setResult(next); + if (next.ok) { + await studio.refetchManifest(); + setMessage(`${next.created ? 'Connected' : 'Updated'} ${next.sourceId} as ${next.kind}.`); + } + } catch (error) { + setMessage(error instanceof Error ? error.message : 'External API connection failed.'); + } finally { + setBusy(false); + } + }, [credentialId, credentialKind, credentialScope, name, protocol, sourceId, studio, url]); + + return ( + + + + + + + + + + + + + + OpenAPI probes direct and conventional schema locations. GraphQL introspection uses the + exact URL. Reusing an ID updates the canonical source. + + + + + + + + + + + + + + + + {message ? {message} : null} + {result ? ( + + ) : null} + + + ); +} diff --git a/src/ui/admin/pages/ManualRestSourceCard.tsx b/src/ui/admin/pages/ManualRestSourceCard.tsx new file mode 100644 index 00000000..62239ecf --- /dev/null +++ b/src/ui/admin/pages/ManualRestSourceCard.tsx @@ -0,0 +1,113 @@ +import type { DataOperationIntent } from '@ankhorage/contracts/data'; +import { Button, Card, Input, Select, Text } from '@ankhorage/zora'; +import { useCallback, useState } from 'react'; +import { View } from 'react-native'; + +import { createManualRestSource } from '../../../externalApiApi'; +import type { ExternalApiConnectResult } from '../../../externalApiAuthoringContracts'; +import { useStudio } from '../../../core/StudioContext'; +import { + ExternalApiDiagnosticList, + ExternalApiField, + externalApiAdminStyles, +} from './ExternalApiAdminPrimitives'; + +const METHOD_OPTIONS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ + value, + label: value, +})); +const INTENT_OPTIONS: readonly { value: DataOperationIntent; label: string }[] = [ + { value: 'read', label: 'Read' }, + { value: 'create', label: 'Create' }, + { value: 'update', label: 'Update' }, + { value: 'delete', label: 'Delete' }, + { value: 'action', label: 'Action' }, +]; + +export function ManualRestSourceCard() { + const studio = useStudio(); + const [sourceId, setSourceId] = useState(''); + const [baseUrl, setBaseUrl] = useState(''); + const [endpointId, setEndpointId] = useState('root'); + const [path, setPath] = useState('/'); + const [operationId, setOperationId] = useState('get-root'); + const [method, setMethod] = useState('GET'); + const [intent, setIntent] = useState('read'); + const [result, setResult] = useState(null); + const [message, setMessage] = useState(null); + const [busy, setBusy] = useState(false); + + const save = useCallback(async () => { + setBusy(true); + setMessage(null); + try { + const next = await createManualRestSource(studio.projectId, { + sourceId, + baseUrl, + endpointId, + path, + operationId, + method, + intent, + }); + setResult(next); + if (next.ok) { + await studio.refetchManifest(); + setMessage(`${next.created ? 'Created' : 'Updated'} manual REST source ${next.sourceId}.`); + } + } catch (error) { + setMessage(error instanceof Error ? error.message : 'Manual REST source creation failed.'); + } finally { + setBusy(false); + } + }, [baseUrl, endpointId, intent, method, operationId, path, sourceId, studio]); + + return ( + + + + Use this when no OpenAPI document exists. Additional operations can be introduced through + the same canonical REST definition architecture. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {message ? {message} : null} + {result ? : null} + + + ); +} diff --git a/src/ui/admin/pages/adminDataSourceOperations.test.ts b/src/ui/admin/pages/adminDataSourceOperations.test.ts new file mode 100644 index 00000000..8b1767e6 --- /dev/null +++ b/src/ui/admin/pages/adminDataSourceOperations.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, test } from 'bun:test'; + +import { collectDataSourceOperationRows } from './adminDataSourceOperations'; + +describe('collectDataSourceOperationRows', () => { + test('lists canonical operation details for API administration', () => { + const rows = collectDataSourceOperationRows({ + inventory: { + id: 'inventory', + kind: 'rest', + baseUrl: 'https://api.example.com', + endpoints: { + items: { + id: 'items', + kind: 'http', + path: '/items', + operations: { + list: { + id: 'list', + name: 'List items', + protocol: 'http', + intent: 'read', + method: 'GET', + }, + }, + }, + }, + }, + }); + + expect(rows).toEqual([ + { + sourceId: 'inventory', + endpointId: 'items', + operationId: 'list', + name: 'List items', + kind: 'read', + protocol: 'http', + method: 'GET', + path: '/items', + }, + ]); + }); +}); diff --git a/src/ui/admin/pages/adminDataSourceOperations.ts b/src/ui/admin/pages/adminDataSourceOperations.ts index 5711b304..7ae082fb 100644 --- a/src/ui/admin/pages/adminDataSourceOperations.ts +++ b/src/ui/admin/pages/adminDataSourceOperations.ts @@ -1,27 +1,30 @@ -import type { DataSourceRegistry } from '@ankhorage/contracts'; - -import { readRecord, readString } from '../adminPageUtils'; +import type { DataSourceRegistry } from '@ankhorage/contracts/data'; export interface DataSourceOperationRow { readonly sourceId: string; readonly endpointId: string; readonly operationId: string; + readonly name?: string; readonly kind: string; readonly protocol: string | null; + readonly method: string | null; + readonly path: string | null; } export function collectDataSourceOperationRows( dataSources: DataSourceRegistry, ): DataSourceOperationRow[] { return Object.entries(dataSources).flatMap(([sourceId, source]) => - Object.entries(readRecord(source).endpoints ?? {}).flatMap(([endpointId, endpoint]) => - Object.entries(readRecord(endpoint).operations ?? {}).map(([operationId, operation]) => ({ + Object.entries(source.endpoints).flatMap(([endpointId, endpoint]) => + Object.entries(endpoint.operations).map(([operationId, operation]) => ({ sourceId, endpointId, operationId, - kind: readString(readRecord(operation).intent) ?? 'operation', - protocol: - readString(readRecord(operation).protocol) ?? readString(readRecord(endpoint).kind), + name: operation.name, + kind: operation.intent, + protocol: operation.protocol, + method: operation.method ?? null, + path: operation.path ?? endpoint.path ?? null, })), ), ); diff --git a/src/upsertExternalApiDataSource.ts b/src/upsertExternalApiDataSource.ts new file mode 100644 index 00000000..aa33fa5d --- /dev/null +++ b/src/upsertExternalApiDataSource.ts @@ -0,0 +1,16 @@ +import type { DataSourceConfig, DataSourceRegistry } from '@ankhorage/contracts/data'; + +export interface ExternalApiDataSourceUpsertResult { + readonly registry: DataSourceRegistry; + readonly created: boolean; +} + +export function upsertExternalApiDataSource( + registry: DataSourceRegistry, + source: DataSourceConfig, +): ExternalApiDataSourceUpsertResult { + return { + registry: { ...registry, [source.id]: source }, + created: registry[source.id] === undefined, + }; +} diff --git a/tsconfig.build.json b/tsconfig.build.json index 14b4c620..b5d12a0a 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -13,6 +13,7 @@ "include": [ "src/root.ts", "src/index.ts", + "src/externalApiAuthoring.ts", "src/app/index.ts", "src/app/StudioApp.tsx", "src/app/ProjectsOverviewScreen.tsx",