Route repository aliases through ServerApi

This commit is contained in:
2026-09-18 17:35:27 +09:00
parent 91f6589e1f
commit 82c191d637
3 changed files with 170 additions and 4 deletions
+12 -1
View File
@@ -98,6 +98,9 @@ pub trait ServerApi {
#[path] workspace_id: String,
) -> Result<RepositoryListResponse, RepositoryApiError>;
#[get("/api/repositories", status = 200, error_status = 400)]
async fn repository_list_alias(&self) -> Result<RepositoryListResponse, RepositoryApiError>;
#[get(
"/api/w/{workspace_id}/repositories/{repository_key}",
status = 200,
@@ -109,6 +112,12 @@ pub trait ServerApi {
#[path] repository_key: String,
) -> Result<RepositoryDetailResponse, RepositoryApiError>;
#[get("/api/repositories/{repository_key}", status = 200, error_status = 404)]
async fn repository_detail_alias(
&self,
#[path] repository_key: String,
) -> Result<RepositoryDetailResponse, RepositoryApiError>;
#[post(
"/api/w/{workspace_id}/repositories",
status = 201,
@@ -3759,7 +3768,7 @@ mod tests {
#[test]
fn worker_session_contract_and_flattened_availability_are_stable() {
let operations = ServerApiMetadata::OPERATIONS;
assert_eq!(operations.len(), 4);
assert_eq!(operations.len(), 6);
let operation = operations
.iter()
.find(|operation| operation.operation_id == "worker_session")
@@ -5143,6 +5152,8 @@ mod openapi_artifact_tests {
["application/json"]["schema"]["$ref"],
"#/components/schemas/RepositoryListResponse"
);
assert!(value["paths"]["/api/repositories"]["get"].is_object());
assert!(value["paths"]["/api/repositories/{repository_key}"]["get"].is_object());
assert!(
value["paths"]["/api/w/{workspace_id}/repositories"]["post"]["responses"]["200"]
.is_object()
+91 -2
View File
@@ -3089,6 +3089,16 @@ impl server_api::ServerApi for WorkspaceApi {
.map_err(ApiError::into_repository_api_error)
}
async fn repository_list_alias(
&self,
) -> std::result::Result<server_api::RepositoryListResponse, server_api::RepositoryApiError>
{
list_repositories(State(self.clone()))
.await
.map(|Json(response)| response)
.map_err(ApiError::into_repository_api_error)
}
async fn repository_detail(
&self,
workspace_id: String,
@@ -3107,6 +3117,17 @@ impl server_api::ServerApi for WorkspaceApi {
.map_err(ApiError::into_repository_api_error)
}
async fn repository_detail_alias(
&self,
repository_key: String,
) -> std::result::Result<server_api::RepositoryDetailResponse, server_api::RepositoryApiError>
{
repository_detail(State(self.clone()), AxumPath(repository_key))
.await
.map(|Json(response)| response)
.map_err(ApiError::into_repository_api_error)
}
async fn repository_create(
&self,
actor: RequestActor,
@@ -3448,8 +3469,6 @@ fn build_inner_router(api: WorkspaceApi) -> Router {
"/api/w/{workspace_id}/objectives/{objective_id}/ticket-links/{ticket_id}",
delete(scoped_unlink_objective_ticket),
)
.route("/api/repositories", get(list_repositories))
.route("/api/repositories/{repository_key}", get(repository_detail))
.route(
"/api/w/{workspace_id}/repositories/{repository_key}/ssh-connection-test",
post(scoped_probe_repository_ssh_connection)
@@ -19047,6 +19066,28 @@ mod tests {
SqliteWorkspaceStore, UserRecord, WorkspaceRecord, WorkspaceRuntimeBinding,
};
#[derive(Clone)]
struct TestBearerAuthorizer(&'static str);
impl server_api::client_support::RequestAuthorizer for TestBearerAuthorizer {
fn authorize(
&self,
_request: server_api::client_support::AuthorizerRequest<'_>,
) -> std::result::Result<
axum::http::HeaderMap,
server_api::client_support::AuthorizationError,
> {
let mut headers = axum::http::HeaderMap::new();
headers.insert(
axum::http::header::AUTHORIZATION,
format!("Bearer {}", self.0)
.parse()
.map_err(|_| server_api::client_support::AuthorizationError::new())?,
);
Ok(headers)
}
}
#[test]
fn backend_resource_timeout_maps_to_gateway_timeout() {
assert_eq!(
@@ -22968,6 +23009,54 @@ mod tests {
String::from_utf8_lossy(&missing_body)
);
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let api_base_url = format!("http://{}", listener.local_addr().unwrap());
let network_app = app.clone();
let server = tokio::spawn(async move {
axum::serve(listener, network_app).await.unwrap();
});
let generated = server_api::ServerApiClient::builder(&api_base_url)
.unwrap()
.authorizer(TestBearerAuthorizer("api-token-auth"))
.build()
.unwrap();
let generated_workspace_id = workspace.workspace.workspace_id.clone();
let generated_list = generated
.repository_list(generated_workspace_id.clone())
.await
.unwrap();
assert!(
generated_list
.items
.iter()
.any(|repository| repository.repository_key == "documentation")
);
assert_eq!(
generated
.repository_detail(generated_workspace_id.clone(), "documentation".to_owned())
.await
.unwrap()
.item
.repository_key,
"documentation"
);
let generated_request = CreateWorkspaceRepositoryRequest {
repository_key: "generated-client".to_owned(),
source: "/srv/repos/generated-client".to_owned(),
default_ref: Some("main".to_owned()),
};
let created = generated
.repository_create(generated_workspace_id.clone(), generated_request.clone())
.await
.unwrap();
assert!(!created.replayed);
let replayed = generated
.repository_create(generated_workspace_id, generated_request)
.await
.unwrap();
assert!(replayed.replayed);
server.abort();
let identity_uri = format!(
"/api/w/{}/settings/signing-identity",
workspace.workspace.workspace_id
+67 -1
View File
@@ -342,10 +342,76 @@
"info": {
"title": "Yoi Server API",
"version": "0.1.0",
"x-yoi-source-digest": "sha256:c7a6b25e1a071ab9a6831e6cd53daf51acdf8aaac20746b0b6db35c12428836f"
"x-yoi-source-digest": "sha256:317cc65b39674bd229abc364d2daea3e909215471da5828313b3cf3ee2142600"
},
"openapi": "3.1.0",
"paths": {
"/api/repositories": {
"get": {
"operationId": "repository_list_alias",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RepositoryListResponse"
}
}
},
"description": "Successful response"
},
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RepositoryApiError"
}
}
},
"description": "Error response"
}
}
}
},
"/api/repositories/{repository_key}": {
"get": {
"operationId": "repository_detail_alias",
"parameters": [
{
"explode": false,
"in": "path",
"name": "repository_key",
"required": true,
"schema": {
"$ref": "#/components/schemas/string"
},
"style": "simple"
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RepositoryDetailResponse"
}
}
},
"description": "Successful response"
},
"404": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RepositoryApiError"
}
}
},
"description": "Error response"
}
}
}
},
"/api/w/{workspace_id}/repositories": {
"get": {
"operationId": "repository_list",