Skip to content

Commit 387e58c

Browse files
authored
Add OpenAPI documentation for GET /api/v1/crates/{name}/{version} response payload (#10709)
1 parent ba2acce commit 387e58c

File tree

3 files changed

+373
-10
lines changed

3 files changed

+373
-10
lines changed

src/controllers/version/metadata.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
//! index or cached metadata which was extracted (client side) from the
55
//! `Cargo.toml` file.
66
7-
use axum_extra::json;
8-
use axum_extra::response::ErasedJson;
7+
use axum::Json;
98

109
use crate::app::AppState;
1110
use crate::models::VersionOwnerAction;
@@ -14,15 +13,20 @@ use crate::views::EncodableVersion;
1413

1514
use super::CrateVersionPath;
1615

16+
#[derive(Debug, Serialize, utoipa::ToSchema)]
17+
pub struct GetResponse {
18+
pub version: EncodableVersion,
19+
}
20+
1721
/// Get crate version metadata.
1822
#[utoipa::path(
1923
get,
2024
path = "/api/v1/crates/{name}/{version}",
2125
params(CrateVersionPath),
2226
tag = "versions",
23-
responses((status = 200, description = "Successful Response")),
27+
responses((status = 200, description = "Successful Response", body = inline(GetResponse))),
2428
)]
25-
pub async fn find_version(state: AppState, path: CrateVersionPath) -> AppResult<ErasedJson> {
29+
pub async fn find_version(state: AppState, path: CrateVersionPath) -> AppResult<Json<GetResponse>> {
2630
let mut conn = state.db_read().await?;
2731
let (version, krate) = path.load_version_and_crate(&mut conn).await?;
2832
let (actions, published_by) = tokio::try_join!(
@@ -31,5 +35,5 @@ pub async fn find_version(state: AppState, path: CrateVersionPath) -> AppResult<
3135
)?;
3236

3337
let version = EncodableVersion::from(version, &krate.name, published_by, actions);
34-
Ok(json!({ "version": version }))
38+
Ok(Json(GetResponse { version }))
3539
}

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,255 @@ expression: response.json()
349349
"url"
350350
],
351351
"type": "object"
352+
},
353+
"Version": {
354+
"properties": {
355+
"audit_actions": {
356+
"description": "A list of actions performed on this version.",
357+
"items": {
358+
"properties": {
359+
"action": {
360+
"description": "The action that was performed.",
361+
"example": "publish",
362+
"type": "string"
363+
},
364+
"time": {
365+
"description": "The date and time the action was performed.",
366+
"example": "2019-12-13T13:46:41Z",
367+
"format": "date-time",
368+
"type": "string"
369+
},
370+
"user": {
371+
"$ref": "#/components/schemas/User",
372+
"description": "The user who performed the action."
373+
}
374+
},
375+
"required": [
376+
"action",
377+
"user",
378+
"time"
379+
],
380+
"type": "object"
381+
},
382+
"type": "array"
383+
},
384+
"bin_names": {
385+
"description": "The names of the binaries provided by this version, if any.",
386+
"example": [],
387+
"items": {
388+
"type": [
389+
"string",
390+
"null"
391+
]
392+
},
393+
"type": [
394+
"array",
395+
"null"
396+
]
397+
},
398+
"checksum": {
399+
"description": "The SHA256 checksum of the compressed crate file encoded as a\nhexadecimal string.",
400+
"example": "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60",
401+
"type": "string"
402+
},
403+
"crate": {
404+
"description": "The name of the crate.",
405+
"example": "serde",
406+
"type": "string"
407+
},
408+
"crate_size": {
409+
"description": "The size of the compressed crate file in bytes.",
410+
"example": 1234,
411+
"format": "int32",
412+
"type": "integer"
413+
},
414+
"created_at": {
415+
"description": "The date and time this version was created.",
416+
"example": "2019-12-13T13:46:41Z",
417+
"format": "date-time",
418+
"type": "string"
419+
},
420+
"description": {
421+
"description": "The description of this version of the crate.",
422+
"example": "A generic serialization/deserialization framework",
423+
"type": [
424+
"string",
425+
"null"
426+
]
427+
},
428+
"dl_path": {
429+
"description": "The API path to download the crate.",
430+
"example": "/api/v1/crates/serde/1.0.0/download",
431+
"type": "string"
432+
},
433+
"documentation": {
434+
"description": "The URL to the crate's documentation, if set.",
435+
"example": "https://docs.rs/serde",
436+
"type": [
437+
"string",
438+
"null"
439+
]
440+
},
441+
"downloads": {
442+
"description": "The total number of downloads for this version.",
443+
"example": 123456,
444+
"format": "int32",
445+
"type": "integer"
446+
},
447+
"edition": {
448+
"description": "The Rust Edition used to compile this version, if set.",
449+
"example": "2021",
450+
"type": [
451+
"string",
452+
"null"
453+
]
454+
},
455+
"features": {
456+
"description": "The features defined by this version.",
457+
"type": "object"
458+
},
459+
"has_lib": {
460+
"description": "Whether this version can be used as a library.",
461+
"example": true,
462+
"type": [
463+
"boolean",
464+
"null"
465+
]
466+
},
467+
"homepage": {
468+
"description": "The URL to the crate's homepage, if set.",
469+
"example": "https://serde.rs",
470+
"type": [
471+
"string",
472+
"null"
473+
]
474+
},
475+
"id": {
476+
"description": "An opaque identifier for the version.",
477+
"example": 42,
478+
"format": "int32",
479+
"type": "integer"
480+
},
481+
"lib_links": {
482+
"description": "The name of the native library this version links with, if any.",
483+
"example": "git2",
484+
"type": [
485+
"string",
486+
"null"
487+
]
488+
},
489+
"license": {
490+
"description": "The license of this version of the crate.",
491+
"example": "MIT",
492+
"type": [
493+
"string",
494+
"null"
495+
]
496+
},
497+
"links": {
498+
"$ref": "#/components/schemas/VersionLinks",
499+
"description": "Links to other API endpoints related to this version."
500+
},
501+
"num": {
502+
"description": "The version number.",
503+
"example": "1.0.0",
504+
"type": "string"
505+
},
506+
"published_by": {
507+
"oneOf": [
508+
{
509+
"type": "null"
510+
},
511+
{
512+
"$ref": "#/components/schemas/User",
513+
"description": "The user who published this version.\n\nThis field may be `null` if the version was published before crates.io\nstarted recording this information."
514+
}
515+
]
516+
},
517+
"readme_path": {
518+
"description": "The API path to download the crate's README file as HTML code.",
519+
"example": "/api/v1/crates/serde/1.0.0/readme",
520+
"type": "string"
521+
},
522+
"repository": {
523+
"description": "The URL to the crate's repository, if set.",
524+
"example": "https://github.com/serde-rs/serde",
525+
"type": [
526+
"string",
527+
"null"
528+
]
529+
},
530+
"rust_version": {
531+
"description": "The minimum version of the Rust compiler required to compile\nthis version, if set.",
532+
"example": "1.31",
533+
"type": [
534+
"string",
535+
"null"
536+
]
537+
},
538+
"updated_at": {
539+
"description": "The date and time this version was last updated (i.e. yanked or unyanked).",
540+
"example": "2019-12-13T13:46:41Z",
541+
"format": "date-time",
542+
"type": "string"
543+
},
544+
"yank_message": {
545+
"description": "The message given when this version was yanked, if any.",
546+
"example": "Security vulnerability",
547+
"type": [
548+
"string",
549+
"null"
550+
]
551+
},
552+
"yanked": {
553+
"description": "Whether this version has been yanked.",
554+
"example": false,
555+
"type": "boolean"
556+
}
557+
},
558+
"required": [
559+
"id",
560+
"crate",
561+
"num",
562+
"dl_path",
563+
"readme_path",
564+
"updated_at",
565+
"created_at",
566+
"downloads",
567+
"features",
568+
"yanked",
569+
"links",
570+
"crate_size",
571+
"audit_actions",
572+
"checksum"
573+
],
574+
"type": "object"
575+
},
576+
"VersionLinks": {
577+
"properties": {
578+
"authors": {
579+
"deprecated": true,
580+
"description": "The API path to download this version's authors.",
581+
"example": "/api/v1/crates/serde/1.0.0/authors",
582+
"type": "string"
583+
},
584+
"dependencies": {
585+
"description": "The API path to download this version's dependencies.",
586+
"example": "/api/v1/crates/serde/1.0.0/dependencies",
587+
"type": "string"
588+
},
589+
"version_downloads": {
590+
"description": "The API path to download this version's download numbers.",
591+
"example": "/api/v1/crates/serde/1.0.0/downloads",
592+
"type": "string"
593+
}
594+
},
595+
"required": [
596+
"dependencies",
597+
"version_downloads",
598+
"authors"
599+
],
600+
"type": "object"
352601
}
353602
},
354603
"securitySchemes": {
@@ -1459,6 +1708,21 @@ expression: response.json()
14591708
],
14601709
"responses": {
14611710
"200": {
1711+
"content": {
1712+
"application/json": {
1713+
"schema": {
1714+
"properties": {
1715+
"version": {
1716+
"$ref": "#/components/schemas/Version"
1717+
}
1718+
},
1719+
"required": [
1720+
"version"
1721+
],
1722+
"type": "object"
1723+
}
1724+
}
1725+
},
14621726
"description": "Successful Response"
14631727
}
14641728
},

0 commit comments

Comments
 (0)