Resolve templated connectors from workspace directory

This commit is contained in:
Noah MacCallum
2026-05-29 15:04:17 -07:00
parent 25d081d10b
commit 4cd3ab4a3d
5 changed files with 343 additions and 175 deletions
+26 -10
View File
@@ -489,9 +489,17 @@ struct RemotePluginMutationResponse {
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct RemoteTemplateConnectorIdsResponse {
struct RemoteWorkspaceConnectorDirectoryResponse {
#[serde(default)]
connector_ids: Vec<String>,
apps: Vec<RemoteWorkspaceConnectorDirectoryApp>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct RemoteWorkspaceConnectorDirectoryApp {
id: String,
#[serde(alias = "templateId")]
template_id: Option<String>,
visibility: Option<String>,
}
pub async fn fetch_remote_marketplaces(
@@ -1453,11 +1461,20 @@ async fn fetch_template_connector_ids(
auth: &CodexAuth,
template_id: &str,
) -> Result<Vec<String>, RemotePluginCatalogError> {
let url = remote_template_connector_ids_url(config, template_id)?;
let url = remote_workspace_connector_directory_url(config)?;
let client = build_reqwest_client();
let request = authenticated_request(client.get(&url), auth)?;
let response: RemoteTemplateConnectorIdsResponse = send_and_decode(request, &url).await?;
Ok(response.connector_ids)
let response: RemoteWorkspaceConnectorDirectoryResponse =
send_and_decode(request, &url).await?;
Ok(response
.apps
.into_iter()
.filter(|app| {
app.template_id.as_deref() == Some(template_id)
&& !matches!(app.visibility.as_deref(), Some("HIDDEN"))
})
.map(|app| app.id)
.collect())
}
fn remote_plugin_skill_detail_url(
@@ -1481,9 +1498,8 @@ fn remote_plugin_skill_detail_url(
Ok(url.to_string())
}
fn remote_template_connector_ids_url(
fn remote_workspace_connector_directory_url(
config: &RemotePluginServiceConfig,
template_id: &str,
) -> Result<String, RemotePluginCatalogError> {
let mut url = Url::parse(config.chatgpt_base_url.trim_end_matches('/'))
.map_err(RemotePluginCatalogError::InvalidBaseUrl)?;
@@ -1492,11 +1508,11 @@ fn remote_template_connector_ids_url(
.path_segments_mut()
.map_err(|()| RemotePluginCatalogError::InvalidBaseUrlPath)?;
segments.pop_if_empty();
segments.push("ps");
segments.push("connectors");
segments.push("by_template_id");
segments.push(template_id);
segments.push("directory");
segments.push("list_workspace");
}
url.set_query(Some("external_logos=true"));
Ok(url.to_string())
}
+12 -11
View File
@@ -26,13 +26,16 @@ fn app(id: &str) -> AppConnectorId {
async fn resolve_remote_plugin_app_ids_expands_templates_and_dedupes_stably() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path(
"/backend-api/ps/connectors/by_template_id/templated_apps_GitHubEnterprise",
))
.and(path("/backend-api/connectors/directory/list_workspace"))
.and(header("authorization", "Bearer Access Token"))
.and(header("chatgpt-account-id", "account_id"))
.respond_with(ResponseTemplate::new(200).set_body_string(
r#"{"connector_ids":["connector_ghe","asdk_app_ghe","connector_ghe"]}"#,
r#"{"apps":[
{"id":"connector_ghe","template_id":"templated_apps_GitHubEnterprise"},
{"id":"asdk_app_ghe","template_id":"templated_apps_GitHubEnterprise"},
{"id":"asdk_app_other","template_id":"templated_apps_Other"},
{"id":"asdk_app_hidden","template_id":"templated_apps_GitHubEnterprise","visibility":"HIDDEN"}
]}"#,
))
.mount(&server)
.await;
@@ -63,12 +66,12 @@ async fn resolve_remote_plugin_app_ids_expands_templates_and_dedupes_stably() {
async fn resolve_remote_plugin_app_ids_drops_missing_template_mappings() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path(
"/backend-api/ps/connectors/by_template_id/templated_apps_GitHubEnterprise",
))
.and(path("/backend-api/connectors/directory/list_workspace"))
.and(header("authorization", "Bearer Access Token"))
.and(header("chatgpt-account-id", "account_id"))
.respond_with(ResponseTemplate::new(200).set_body_string(r#"{"connector_ids":[]}"#))
.respond_with(ResponseTemplate::new(200).set_body_string(
r#"{"apps":[{"id":"asdk_app_other","template_id":"templated_apps_Other"}]}"#,
))
.mount(&server)
.await;
@@ -86,9 +89,7 @@ async fn resolve_remote_plugin_app_ids_drops_missing_template_mappings() {
async fn resolve_remote_plugin_app_ids_drops_templates_when_lookup_fails() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path(
"/backend-api/ps/connectors/by_template_id/templated_apps_GitHubEnterprise",
))
.and(path("/backend-api/connectors/directory/list_workspace"))
.and(header("authorization", "Bearer Access Token"))
.and(header("chatgpt-account-id", "account_id"))
.respond_with(ResponseTemplate::new(500).set_body_string("lookup failed"))