samling/collections/
routes.rs

1use axum::extract::State;
2use axum::{http::StatusCode, routing, Json};
3
4use super::entities::{Collection, CreateCollection, UpdateCollection};
5use super::repo::CollectionsRepo;
6use crate::auth::signing::Claims;
7use crate::auth::Permission;
8use crate::errors::Result;
9use crate::extractors::{FiltersQuery, PoolClient};
10use crate::organizations::extractors::PathOrganizationId;
11use crate::routes::AppRouter;
12use crate::{CloudflareApi, CollectionFilters, CollectionSummary};
13use crate::{CollectionItem, Filters};
14use crate::{CollectionWithItems, NestedStyleSortOrder, Ref, Style};
15
16pub(crate) fn org_routes() -> AppRouter {
17    AppRouter::new().nest(
18        "/collections",
19        AppRouter::new()
20            .route(
21                "/",
22                routing::get(list_collection_summaries).post(create_collection),
23            )
24            .route("/summaries", routing::get(list_collection_summaries))
25            .route(
26                "/{collection_ref}",
27                routing::get(get_collection)
28                    .patch(update_collection)
29                    .put(upsert_collection)
30                    .delete(delete_collection),
31            )
32            .route(
33                "/{collection_ref}/with-items",
34                routing::get(get_collection_with_items),
35            )
36            .route(
37                "/{collection_ref}/summary",
38                routing::get(get_collection_summary),
39            )
40            .route(
41                "/{collection_ref}/items/{style_ref}",
42                routing::get(get_collection_item),
43            ),
44    )
45}
46
47/// Get collection and its associated styles, with colors and sizes included
48async fn get_collection_with_items(
49    PoolClient(client): PoolClient,
50    claims: Claims,
51    PathOrganizationId(organization_id): PathOrganizationId,
52    collection_ref: Ref<Collection>,
53    FiltersQuery(filters): FiltersQuery<CollectionFilters>,
54    sorter: NestedStyleSortOrder,
55) -> Result<Json<CollectionWithItems>> {
56    let metadata = claims.ensure(organization_id, &[Permission::ListCollectionItems])?;
57
58    let filters = filters.resolve(&client, organization_id).await?;
59    let collection = CollectionsRepo
60        .get_with_items(&client, metadata, &collection_ref, filters, sorter)
61        .await?;
62
63    Ok(Json(collection))
64}
65
66/// Get collection summary
67async fn get_collection_summary(
68    PoolClient(client): PoolClient,
69    claims: Claims,
70    PathOrganizationId(organization_id): PathOrganizationId,
71    collection_ref: Ref<Collection>,
72) -> Result<Json<CollectionSummary>> {
73    let metadata = claims.ensure(organization_id, &[Permission::GetCollectionSummary])?;
74
75    let collection = CollectionsRepo
76        .get_summary(&client, metadata, &collection_ref)
77        .await?;
78
79    Ok(Json(collection))
80}
81
82/// List Collection summaries
83async fn list_collection_summaries(
84    claims: Claims,
85    PathOrganizationId(organization_id): PathOrganizationId,
86    PoolClient(client): PoolClient,
87) -> Result<Json<Vec<CollectionSummary>>> {
88    let metadata = claims.ensure(organization_id, &[Permission::ListCollections])?;
89    let collections = CollectionsRepo.list_summaries(&client, metadata).await?;
90
91    Ok(Json(collections))
92}
93
94/// Get Collection
95async fn get_collection(
96    PathOrganizationId(organization_id): PathOrganizationId,
97    PoolClient(client): PoolClient,
98    claims: Claims,
99    ref_: Ref<Collection>,
100) -> Result<Json<Collection>> {
101    let metadata = claims.ensure(organization_id, &[Permission::GetCollection])?;
102    let res = CollectionsRepo.find(&client, metadata, &ref_).await?;
103    if let Some(collection) = res {
104        Ok(Json(collection))
105    } else {
106        Err(ref_.not_found_error())
107    }
108}
109
110/// Create new Collection
111async fn create_collection(
112    PoolClient(mut client): PoolClient,
113    State(cloudflare_api): State<CloudflareApi>,
114    claims: Claims,
115    PathOrganizationId(organization_id): PathOrganizationId,
116    Json(collection): Json<CreateCollection>,
117) -> Result<Json<Collection>> {
118    let metadata = claims.ensure(organization_id, &[Permission::CreateCollection])?;
119    Ok(Json(
120        CollectionsRepo
121            .insert(&mut client, cloudflare_api, metadata, collection)
122            .await?,
123    ))
124}
125
126/// Update Collection
127async fn update_collection(
128    PoolClient(mut client): PoolClient,
129    State(cloudflare_api): State<CloudflareApi>,
130    claims: Claims,
131    PathOrganizationId(organization_id): PathOrganizationId,
132    ref_: Ref<Collection>,
133    Json(collection): Json<UpdateCollection>,
134) -> Result<Json<Collection>> {
135    let metadata = claims.ensure(organization_id, &[Permission::UpdateCollection])?;
136    Ok(Json(
137        CollectionsRepo
138            .update(&mut client, cloudflare_api, metadata, &ref_, collection)
139            .await?,
140    ))
141}
142
143/// Upsert Collection
144async fn upsert_collection(
145    PoolClient(mut client): PoolClient,
146    State(cloudflare_api): State<CloudflareApi>,
147    claims: Claims,
148    PathOrganizationId(organization_id): PathOrganizationId,
149    ref_: Ref<Collection>,
150    Json(collection): Json<CreateCollection>,
151) -> Result<(StatusCode, Json<Collection>)> {
152    let metadata = claims.ensure(
153        organization_id,
154        &[Permission::CreateCollection, Permission::UpdateCollection],
155    )?;
156    let (created, entity) = CollectionsRepo
157        .upsert(&mut client, cloudflare_api, metadata, &ref_, collection)
158        .await?;
159    if created {
160        Ok((StatusCode::CREATED, Json(entity)))
161    } else {
162        Ok((StatusCode::OK, Json(entity)))
163    }
164}
165
166/// Delete Collection
167async fn delete_collection(
168    PathOrganizationId(organization_id): PathOrganizationId,
169    PoolClient(client): PoolClient,
170    claims: Claims,
171    ref_: Ref<Collection>,
172) -> Result<StatusCode> {
173    claims.ensure(organization_id, &[Permission::DeleteCollection])?;
174    CollectionsRepo
175        .delete(&client, organization_id, &ref_)
176        .await?;
177    Ok(StatusCode::NO_CONTENT)
178}
179
180/// Get style item belonging to a collection, with colors and sizes included
181async fn get_collection_item(
182    PathOrganizationId(organization_id): PathOrganizationId,
183    PoolClient(client): PoolClient,
184    claims: Claims,
185    collection_ref: Ref<Collection>,
186    style_ref: Ref<Style>,
187) -> Result<Json<CollectionItem>> {
188    let metadata = claims.ensure(organization_id, &[Permission::GetCollectionItem])?;
189
190    let item = CollectionsRepo
191        .get_collection_item(&client, metadata, &collection_ref, &style_ref)
192        .await?;
193
194    Ok(Json(item))
195}