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
47async 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
66async 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
82async 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
94async 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
110async 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
126async 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
143async 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
166async 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
180async 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}