mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 10:34:53 +00:00
feat: better api docs (#6586)
* feat: docs * fix tombi * chore: fix some of the routes rendering with missing / * response schemas * fix: restore labrinth docs routes * Fix path parameter docs in routes * remove utoipa-actix-web * consistency * improve version intros * improve formatting, examples * better hash examples, move openapi stuff to openapi.rs * more utoipa param fixes * request body docs * chore: remove moderation route from v2, remove ingest & webhooks from v3 spec * fixes * chore: tweak sources titles * fix * fix test * improve examples * increase compiler spawned thread stack size * remove unused tests & script * test * bro what * fix --------- Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
This commit is contained in:
co-authored by
aecsocket
parent
b26d048a63
commit
4a6fa9fc3d
@@ -42,20 +42,17 @@ use eyre::eyre;
|
||||
use futures::TryStreamExt;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use validator::Validate;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.route("search", web::get().to(project_search));
|
||||
cfg.service(project_search_post);
|
||||
cfg.route("projects", web::get().to(projects_get));
|
||||
cfg.route("projects", web::patch().to(projects_edit));
|
||||
cfg.route("projects_random", web::get().to(random_projects_get));
|
||||
pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
cfg.service(project_search)
|
||||
.service(project_search_post)
|
||||
.service(projects_get_route)
|
||||
.service(projects_edit_route)
|
||||
.service(random_projects_get_route);
|
||||
}
|
||||
|
||||
pub fn utoipa_config(
|
||||
cfg: &mut utoipa_actix_web::service_config::ServiceConfig,
|
||||
) {
|
||||
pub fn project_config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
cfg.service(project_get)
|
||||
.service(project_get_check)
|
||||
.service(project_delete)
|
||||
@@ -99,6 +96,20 @@ pub struct RandomProjects {
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "projects",
|
||||
params(("count" = u32, Query)),
|
||||
responses((status = OK))
|
||||
)]
|
||||
#[get("/projects_random")]
|
||||
pub async fn random_projects_get_route(
|
||||
count: web::Query<RandomProjects>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
random_projects_get(count, pool, redis).await
|
||||
}
|
||||
|
||||
pub async fn random_projects_get(
|
||||
web::Query(count): web::Query<RandomProjects>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -140,11 +151,32 @@ pub async fn random_projects_get(
|
||||
Ok(HttpResponse::Ok().json(projects_data))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ProjectIds {
|
||||
pub ids: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, utoipa::ToSchema)]
|
||||
pub struct ProjectCheckResponse {
|
||||
pub id: ProjectId,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "projects",
|
||||
params(("ids" = String, Query)),
|
||||
responses((status = OK))
|
||||
)]
|
||||
#[get("/projects")]
|
||||
pub async fn projects_get_route(
|
||||
req: HttpRequest,
|
||||
ids: web::Query<ProjectIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
projects_get(req, ids, pool, redis, session_queue).await
|
||||
}
|
||||
|
||||
pub async fn projects_get(
|
||||
req: HttpRequest,
|
||||
web::Query(ids): web::Query<ProjectIds>,
|
||||
@@ -174,9 +206,13 @@ pub async fn projects_get(
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Get a project.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = OK, body = Project))
|
||||
)]
|
||||
#[get("/{id}")]
|
||||
async fn project_get(
|
||||
pub async fn project_get(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -305,9 +341,13 @@ pub struct EditProject {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[utoipa::path]
|
||||
/// Update a project.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[patch("/{id}")]
|
||||
async fn project_edit(
|
||||
pub async fn project_edit(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -1205,6 +1245,27 @@ pub async fn edit_project_categories(
|
||||
// pub total_hits: usize,
|
||||
// }
|
||||
|
||||
/// Search projects.
|
||||
#[utoipa::path(
|
||||
tag = "search",
|
||||
get,
|
||||
operation_id = "v3SearchProjects",
|
||||
params(
|
||||
("query" = Option<String>, Query, description = "The query to search for"),
|
||||
("facets" = Option<String>, Query, description = "Search facets JSON"),
|
||||
("filters" = Option<String>, Query, description = "Search filters JSON"),
|
||||
("new_filters" = Option<String>, Query, description = "Search filters JSON"),
|
||||
("index" = Option<String>, Query, description = "Search index to use"),
|
||||
("offset" = Option<String>, Query, description = "Search result offset"),
|
||||
("limit" = Option<String>, Query, description = "Maximum number of search results"),
|
||||
("version" = Option<String>, Query, description = "Game version to filter for")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Expected response to a valid request", body = SearchResults),
|
||||
(status = 400, description = "Request was invalid, see given error")
|
||||
)
|
||||
)]
|
||||
#[get("/search")]
|
||||
pub async fn project_search(
|
||||
web::Query(info): web::Query<SearchQuery>,
|
||||
search_backend: web::Data<dyn SearchBackend>,
|
||||
@@ -1230,6 +1291,12 @@ pub async fn project_search(
|
||||
}
|
||||
|
||||
// for more complicated search queries
|
||||
/// Search projects.
|
||||
#[utoipa::path(
|
||||
tag = "search",
|
||||
request_body = serde_json::Value,
|
||||
responses((status = OK, body = SearchResults))
|
||||
)]
|
||||
#[post("/search")]
|
||||
pub async fn project_search_post(
|
||||
web::Json(info): web::Json<SearchRequest>,
|
||||
@@ -1241,9 +1308,13 @@ pub async fn project_search_post(
|
||||
}
|
||||
|
||||
//checks the validity of a project id or slug
|
||||
#[utoipa::path]
|
||||
/// Check project availability.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = OK, body = ProjectCheckResponse))
|
||||
)]
|
||||
#[get("/{id}/check")]
|
||||
async fn project_get_check(
|
||||
pub async fn project_get_check(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
@@ -1262,21 +1333,25 @@ pub async fn project_get_check_internal(
|
||||
db_models::DBProject::get(&slug, &**pool, &redis).await?;
|
||||
|
||||
if let Some(project) = project_data {
|
||||
Ok(HttpResponse::Ok().json(json! ({
|
||||
"id": models::ids::ProjectId::from(project.inner.id)
|
||||
})))
|
||||
Ok(HttpResponse::Ok().json(ProjectCheckResponse {
|
||||
id: models::ids::ProjectId::from(project.inner.id),
|
||||
}))
|
||||
} else {
|
||||
Err(ApiError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct DependencyInfo {
|
||||
pub projects: Vec<Project>,
|
||||
pub versions: Vec<models::projects::Version>,
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// List project dependencies.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = OK, body = DependencyInfo))
|
||||
)]
|
||||
#[get("/{project_id}/dependencies")]
|
||||
pub async fn dependency_list(
|
||||
req: HttpRequest,
|
||||
@@ -1400,7 +1475,7 @@ pub struct CategoryChanges<'a> {
|
||||
pub remove_categories: &'a Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Validate)]
|
||||
#[derive(Deserialize, Validate, utoipa::ToSchema)]
|
||||
pub struct BulkEditProject {
|
||||
#[validate(length(max = 3))]
|
||||
pub categories: Option<Vec<String>>,
|
||||
@@ -1420,6 +1495,33 @@ pub struct BulkEditProject {
|
||||
pub link_urls: Option<HashMap<String, Option<String>>>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "projects",
|
||||
params(("ids" = String, Query)),
|
||||
responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[patch("/projects")]
|
||||
pub async fn projects_edit_route(
|
||||
req: HttpRequest,
|
||||
ids: web::Query<ProjectIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
bulk_edit_project: web::Json<BulkEditProject>,
|
||||
redis: web::Data<RedisPool>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
search_state: web::Data<SearchState>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
projects_edit(
|
||||
req,
|
||||
ids,
|
||||
pool,
|
||||
bulk_edit_project,
|
||||
redis,
|
||||
session_queue,
|
||||
search_state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn projects_edit(
|
||||
req: HttpRequest,
|
||||
web::Query(ids): web::Query<ProjectIds>,
|
||||
@@ -1722,9 +1824,18 @@ pub struct Extension {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[utoipa::path]
|
||||
/// Update a project icon.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects",
|
||||
params(
|
||||
("ext" = String, Query)
|
||||
),
|
||||
request_body(content = Vec<u8>, content_type = "application/octet-stream"),
|
||||
responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[patch("/{id}/icon")]
|
||||
async fn project_icon_edit(
|
||||
pub async fn project_icon_edit(
|
||||
web::Query(ext): web::Query<Extension>,
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
@@ -1866,9 +1977,13 @@ pub async fn project_icon_edit_internal(
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Delete a project icon.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[delete("/{id}/icon")]
|
||||
async fn delete_project_icon(
|
||||
pub async fn delete_project_icon(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -1992,7 +2107,20 @@ pub struct GalleryCreateQuery {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[utoipa::path]
|
||||
/// Add a gallery item.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects",
|
||||
params(
|
||||
("ext" = String, Query),
|
||||
("featured" = bool, Query),
|
||||
("name" = Option<String>, Query),
|
||||
("description" = Option<String>, Query),
|
||||
("ordering" = Option<i64>, Query)
|
||||
),
|
||||
request_body(content = Vec<u8>, content_type = "application/octet-stream"),
|
||||
responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[post("/{id}/gallery")]
|
||||
pub async fn add_gallery_item(
|
||||
web::Query(ext): web::Query<Extension>,
|
||||
@@ -2190,9 +2318,21 @@ pub struct GalleryEditQuery {
|
||||
pub ordering: Option<i64>,
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Update a gallery item.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects",
|
||||
params(
|
||||
("url" = String, Query),
|
||||
("featured" = Option<bool>, Query),
|
||||
("name" = Option<String>, Query),
|
||||
("description" = Option<String>, Query),
|
||||
("ordering" = Option<i64>, Query)
|
||||
),
|
||||
responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[patch("/{id}/gallery")]
|
||||
async fn edit_gallery_item(
|
||||
pub async fn edit_gallery_item(
|
||||
req: HttpRequest,
|
||||
web::Query(item): web::Query<GalleryEditQuery>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -2379,9 +2519,17 @@ pub struct GalleryDeleteQuery {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Delete a gallery item.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects",
|
||||
params(
|
||||
("url" = String, Query)
|
||||
),
|
||||
responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[delete("/{id}/gallery")]
|
||||
async fn delete_gallery_item(
|
||||
pub async fn delete_gallery_item(
|
||||
req: HttpRequest,
|
||||
web::Query(item): web::Query<GalleryDeleteQuery>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -2514,9 +2662,13 @@ pub async fn delete_gallery_item_internal(
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Delete a project.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[delete("/{id}")]
|
||||
async fn project_delete(
|
||||
pub async fn project_delete(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -2672,9 +2824,13 @@ pub async fn project_delete_internal(
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Follow a project.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[post("/{id}/follow")]
|
||||
async fn project_follow(
|
||||
pub async fn project_follow(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -2764,9 +2920,13 @@ pub async fn project_follow_internal(
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Unfollow a project.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = NO_CONTENT))
|
||||
)]
|
||||
#[delete("/{id}/follow")]
|
||||
async fn project_unfollow(
|
||||
pub async fn project_unfollow(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
@@ -2852,7 +3012,11 @@ pub async fn project_unfollow_internal(
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
/// Get a project's organization.
|
||||
#[utoipa::path(
|
||||
context_path = "/project",
|
||||
tag = "projects", responses((status = OK, body = models::organizations::Organization))
|
||||
)]
|
||||
#[get("/{id}/organization")]
|
||||
pub async fn project_get_organization(
|
||||
req: HttpRequest,
|
||||
|
||||
Reference in New Issue
Block a user