refactor: name the workspace catalog list response
This commit is contained in:
@@ -2,7 +2,10 @@ use crate::{BackendApiClient, BackendApiClientError};
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use workspace_api::{WorkspaceCreateResponse, WorkspaceRepositoryRecord, WorkspaceSummary};
|
||||
use workspace_api::{
|
||||
WorkspaceCatalogListResponse, WorkspaceCreateResponse, WorkspaceRepositoryRecord,
|
||||
WorkspaceSummary,
|
||||
};
|
||||
|
||||
const DEFAULT_WORKSPACE_LIMIT: usize = 200;
|
||||
|
||||
@@ -88,7 +91,7 @@ async fn list_backend_workspaces_with_client(
|
||||
.send()
|
||||
.await?;
|
||||
client.check_status(response.status())?;
|
||||
Ok(response.json::<Vec<BackendWorkspace>>().await?)
|
||||
Ok(response.json::<WorkspaceCatalogListResponse>().await?.0)
|
||||
}
|
||||
|
||||
pub async fn create_backend_workspace(
|
||||
|
||||
@@ -105,6 +105,14 @@ pub struct WorkspaceSummary {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
/// Public response returned by `GET /api/workspaces`.
|
||||
///
|
||||
/// The transparent newtype keeps the established top-level JSON array while making the
|
||||
/// complete list response a named cross-crate and generated-TypeScript authority.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||
pub struct WorkspaceCatalogListResponse(pub Vec<WorkspaceSummary>);
|
||||
|
||||
/// Public Repository record embedded in Workspace creation responses.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||
@@ -754,6 +762,7 @@ pub fn catalog_typescript() -> String {
|
||||
let config = ts_rs::Config::default();
|
||||
let declarations = [
|
||||
WorkspaceSummary::decl(&config),
|
||||
WorkspaceCatalogListResponse::decl(&config),
|
||||
WorkspaceRepositoryRecord::decl(&config),
|
||||
WorkspaceCreateResponse::decl(&config),
|
||||
WorkspaceAuthConfig::decl(&config),
|
||||
@@ -814,6 +823,17 @@ mod tests {
|
||||
let parsed: WorkspaceResponse = serde_json::from_value(workspace.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(parsed).unwrap(), workspace);
|
||||
|
||||
let catalog = serde_json::json!([{
|
||||
"workspace_id": "workspace-test",
|
||||
"owner_account_id": "user-test",
|
||||
"display_name": "Test",
|
||||
"state": "active",
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": "2026-01-01T00:00:00Z"
|
||||
}]);
|
||||
let parsed: WorkspaceCatalogListResponse = serde_json::from_value(catalog.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(parsed).unwrap(), catalog);
|
||||
|
||||
let repositories = serde_json::json!({
|
||||
"workspace_id": "workspace-test",
|
||||
"items": [{
|
||||
@@ -860,6 +880,9 @@ mod tests {
|
||||
#[test]
|
||||
fn generated_catalog_typescript_keeps_public_wrappers_and_nullability() {
|
||||
let output = catalog_typescript();
|
||||
assert!(
|
||||
output.contains("export type WorkspaceCatalogListResponse = Array<WorkspaceSummary>")
|
||||
);
|
||||
assert!(output.contains("export type WorkspaceResponse ="));
|
||||
assert!(output.contains("permissions: WorkspacePermissionSummary"));
|
||||
assert!(output.contains("export type RepositoryListResponse ="));
|
||||
|
||||
@@ -67,10 +67,10 @@ use workspace_api::{
|
||||
RepositoryDetailResponse, RepositoryListResponse, RepositoryLogResponse,
|
||||
RepositorySshCredential, RepositorySshHostTrust, RotateRepositorySshCredentialRequest,
|
||||
RuntimeConnectionTestResponse, RuntimeManagementSummary, TICKET_ORCHESTRATION_PLANS_QUERY_PATH,
|
||||
TICKET_RELATIONS_QUERY_PATH, WorkspaceCreateResponse, WorkspaceExtensionPointState,
|
||||
WorkspaceExtensionPoints, WorkspacePermissionSummary, WorkspaceRepositoryRecord,
|
||||
WorkspaceResponse, WorkspaceRuntimeResource, WorkspaceSummary, WorkspaceWorkerDiscoveryItem,
|
||||
WorkspaceWorkerDiscoveryPage, WorkspaceWorkerSubject,
|
||||
TICKET_RELATIONS_QUERY_PATH, WorkspaceCatalogListResponse, WorkspaceCreateResponse,
|
||||
WorkspaceExtensionPointState, WorkspaceExtensionPoints, WorkspacePermissionSummary,
|
||||
WorkspaceRepositoryRecord, WorkspaceResponse, WorkspaceRuntimeResource, WorkspaceSummary,
|
||||
WorkspaceWorkerDiscoveryItem, WorkspaceWorkerDiscoveryPage, WorkspaceWorkerSubject,
|
||||
};
|
||||
|
||||
use crate::auth::{
|
||||
@@ -980,7 +980,9 @@ async fn list_server_workspaces(
|
||||
let owner = match resolve_server_actor(&api, &headers).await {
|
||||
Ok(Some(actor)) => Some(actor.account_id),
|
||||
Ok(None) => match api.catalog.is_empty() {
|
||||
Ok(true) => return Json(Vec::<WorkspaceSummary>::new()).into_response(),
|
||||
Ok(true) => {
|
||||
return Json(WorkspaceCatalogListResponse(Vec::new())).into_response();
|
||||
}
|
||||
Ok(false) => return StatusCode::UNAUTHORIZED.into_response(),
|
||||
Err(error) => return server_error_response(error),
|
||||
},
|
||||
@@ -990,12 +992,9 @@ async fn list_server_workspaces(
|
||||
.catalog
|
||||
.list(owner.as_deref(), query.limit.unwrap_or(100))
|
||||
{
|
||||
Ok(workspaces) => Json(
|
||||
workspaces
|
||||
.into_iter()
|
||||
.map(workspace_summary)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
Ok(workspaces) => Json(WorkspaceCatalogListResponse(
|
||||
workspaces.into_iter().map(workspace_summary).collect(),
|
||||
))
|
||||
.into_response(),
|
||||
Err(error) => server_error_response(error),
|
||||
}
|
||||
@@ -17784,10 +17783,10 @@ mod tests {
|
||||
let catalog_body = to_bytes(authenticated_catalog.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
let typed_catalog: Vec<workspace_api::WorkspaceSummary> =
|
||||
let typed_catalog: workspace_api::WorkspaceCatalogListResponse =
|
||||
serde_json::from_slice(&catalog_body).unwrap();
|
||||
assert_eq!(
|
||||
typed_catalog[0].workspace_id,
|
||||
typed_catalog.0[0].workspace_id,
|
||||
workspace.workspace.workspace_id
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ export type WorkspaceSummary = {
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type WorkspaceCatalogListResponse = Array<WorkspaceSummary>;
|
||||
|
||||
export type WorkspaceRepositoryRecord = {
|
||||
workspace_id: string;
|
||||
repository_id: string;
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
RepositorySourceKind,
|
||||
RepositorySummary,
|
||||
WorkspaceAuthConfig,
|
||||
WorkspaceCatalogListResponse,
|
||||
WorkspaceCreateResponse,
|
||||
WorkspaceExtensionPoints,
|
||||
WorkspaceExtensionPointState,
|
||||
@@ -28,6 +29,7 @@ export type {
|
||||
RepositoryListResponse,
|
||||
RepositoryLogResponse,
|
||||
RepositorySummary,
|
||||
WorkspaceCatalogListResponse,
|
||||
WorkspaceCreateResponse,
|
||||
WorkspacePermissionSummary,
|
||||
WorkspaceResponse,
|
||||
@@ -412,7 +414,7 @@ function commitSummary(value: unknown, path: string): GitCommitSummary {
|
||||
|
||||
export function parseWorkspaceCatalogResponse(
|
||||
value: unknown,
|
||||
): WorkspaceSummary[] {
|
||||
): WorkspaceCatalogListResponse {
|
||||
return array(value, "workspaces").map((item, index) =>
|
||||
workspaceSummary(item, `workspaces[${index}]`)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user