Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions src/controllers/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ use crate::controllers::helpers::{Paginate, pagination::Paginated};
use crate::models::Keyword;
use crate::util::errors::AppResult;
use crate::views::EncodableKeyword;
use axum::Json;
use axum::extract::{FromRequestParts, Path, Query};
use axum_extra::json;
use axum_extra::response::ErasedJson;
use diesel::prelude::*;
use http::request::Parts;

Expand All @@ -22,19 +21,35 @@ pub struct ListQueryParams {
sort: Option<String>,
}

#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct ListResponse {
/// The list of keywords.
pub keywords: Vec<EncodableKeyword>,

#[schema(inline)]
pub meta: ListMeta,
}

#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct ListMeta {
/// The total number of keywords.
#[schema(example = 123)]
pub total: i64,
}

/// List all keywords.
#[utoipa::path(
get,
path = "/api/v1/keywords",
params(ListQueryParams, PaginationQueryParams),
tag = "keywords",
responses((status = 200, description = "Successful Response")),
responses((status = 200, description = "Successful Response", body = inline(ListResponse))),
)]
pub async fn list_keywords(
state: AppState,
params: ListQueryParams,
req: Parts,
) -> AppResult<ErasedJson> {
) -> AppResult<Json<ListResponse>> {
use crate::schema::keywords;

let mut query = keywords::table.into_boxed();
Expand All @@ -49,15 +64,15 @@ pub async fn list_keywords(
let mut conn = state.db_read().await?;
let data: Paginated<Keyword> = query.load(&mut conn).await?;
let total = data.total();
let kws = data
.into_iter()
.map(Keyword::into)
.collect::<Vec<EncodableKeyword>>();
let keywords = data.into_iter().map(Keyword::into).collect();

Ok(json!({
"keywords": kws,
"meta": { "total": total },
}))
let meta = ListMeta { total };
Ok(Json(ListResponse { keywords, meta }))
}

#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct GetResponse {
pub keyword: EncodableKeyword,
}

/// Get keyword metadata.
Expand All @@ -68,11 +83,14 @@ pub async fn list_keywords(
("keyword" = String, Path, description = "The keyword to find"),
),
tag = "keywords",
responses((status = 200, description = "Successful Response")),
responses((status = 200, description = "Successful Response", body = inline(GetResponse))),
)]
pub async fn find_keyword(Path(name): Path<String>, state: AppState) -> AppResult<ErasedJson> {
pub async fn find_keyword(
Path(name): Path<String>,
state: AppState,
) -> AppResult<Json<GetResponse>> {
let mut conn = state.db_read().await?;
let kw = Keyword::find_by_keyword(&mut conn, &name).await?;

Ok(json!({ "keyword": EncodableKeyword::from(kw) }))
let keyword = EncodableKeyword::from(kw);
Ok(Json(GetResponse { keyword }))
}
5 changes: 5 additions & 0 deletions src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ other clients).
license(name = "MIT OR Apache-2.0", url = "https://github.com/rust-lang/crates.io/blob/main/README.md#%EF%B8%8F-license"),
version = "0.0.0",
),
components(
schemas(
crate::views::EncodableKeyword,
),
),
modifiers(&SecurityAddon),
servers(
(url = "https://crates.io"),
Expand Down
84 changes: 84 additions & 0 deletions src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@ expression: response.json()
---
{
"components": {
"schemas": {
"Keyword": {
"properties": {
"crates_cnt": {
"description": "The total number of crates that have this keyword.",
"example": 42,
"format": "int32",
"type": "integer"
},
"created_at": {
"description": "The date and time this keyword was created.",
"example": "2017-01-06T14:23:11Z",
"format": "date-time",
"type": "string"
},
"id": {
"description": "An opaque identifier for the keyword.",
"example": "http",
"type": "string"
},
"keyword": {
"description": "The keyword itself.",
"example": "http",
"type": "string"
}
},
"required": [
"id",
"keyword",
"created_at",
"crates_cnt"
],
"type": "object"
}
},
"securitySchemes": {
"api_token": {
"description": "The API token is used to authenticate requests from cargo and other clients.",
Expand Down Expand Up @@ -1331,6 +1366,40 @@ expression: response.json()
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"properties": {
"keywords": {
"description": "The list of keywords.",
"items": {
"$ref": "#/components/schemas/Keyword"
},
"type": "array"
},
"meta": {
"properties": {
"total": {
"description": "The total number of keywords.",
"example": 123,
"format": "int64",
"type": "integer"
}
},
"required": [
"total"
],
"type": "object"
}
},
"required": [
"keywords",
"meta"
],
"type": "object"
}
}
},
"description": "Successful Response"
}
},
Expand All @@ -1356,6 +1425,21 @@ expression: response.json()
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"properties": {
"keyword": {
"$ref": "#/components/schemas/Keyword"
}
},
"required": [
"keyword"
],
"type": "object"
}
}
},
"description": "Successful Response"
}
},
Expand Down
14 changes: 13 additions & 1 deletion src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,23 @@ impl From<VersionDownload> for EncodableVersionDownload {
}
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, utoipa::ToSchema)]
#[schema(as = Keyword)]
pub struct EncodableKeyword {
/// An opaque identifier for the keyword.
#[schema(example = "http")]
pub id: String,

/// The keyword itself.
#[schema(example = "http")]
pub keyword: String,

/// The date and time this keyword was created.
#[schema(example = "2017-01-06T14:23:11Z")]
pub created_at: DateTime<Utc>,

/// The total number of crates that have this keyword.
#[schema(example = 42)]
pub crates_cnt: i32,
}

Expand Down