Skip to content

Commit be8b9ee

Browse files
committed
Add API endpoint to GET single public collection
1 parent 026234b commit be8b9ee

File tree

2 files changed

+120
-9
lines changed

2 files changed

+120
-9
lines changed

backend/btrixcloud/colls.py

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,11 @@ async def remove_crawls_from_collection(
210210
return await self.get_collection_out(coll_id, org)
211211

212212
async def get_collection_raw(
213-
self, coll_id: UUID, public_only: bool = False
213+
self, coll_id: UUID, public_or_unlisted_only: bool = False
214214
) -> Dict[str, Any]:
215215
"""Get collection by id as dict from database"""
216216
query: dict[str, object] = {"_id": coll_id}
217-
if public_only:
217+
if public_or_unlisted_only:
218218
query["access"] = {"$in": ["public", "unlisted"]}
219219

220220
result = await self.collections.find_one(query)
@@ -224,17 +224,21 @@ async def get_collection_raw(
224224
return result
225225

226226
async def get_collection(
227-
self, coll_id: UUID, public_only: bool = False
227+
self, coll_id: UUID, public_or_unlisted_only: bool = False
228228
) -> Collection:
229229
"""Get collection by id"""
230-
result = await self.get_collection_raw(coll_id, public_only)
230+
result = await self.get_collection_raw(coll_id, public_or_unlisted_only)
231231
return Collection.from_dict(result)
232232

233233
async def get_collection_out(
234-
self, coll_id: UUID, org: Organization, resources=False, public_only=False
234+
self,
235+
coll_id: UUID,
236+
org: Organization,
237+
resources=False,
238+
public_or_unlisted_only=False,
235239
) -> CollOut:
236240
"""Get CollOut by id"""
237-
result = await self.get_collection_raw(coll_id, public_only)
241+
result = await self.get_collection_raw(coll_id, public_or_unlisted_only)
238242

239243
if resources:
240244
result["resources"] = await self.get_collection_crawl_resources(coll_id)
@@ -248,6 +252,26 @@ async def get_collection_out(
248252

249253
return CollOut.from_dict(result)
250254

255+
async def get_public_collection_out(
256+
self, coll_id: UUID, org: Organization
257+
) -> CollOut:
258+
"""Get PublicCollOut by id"""
259+
result = await self.get_collection_raw(coll_id)
260+
261+
if result.get("access") != "public":
262+
raise HTTPException(status_code=404, detail="collection_not_found")
263+
264+
result["resources"] = await self.get_collection_crawl_resources(coll_id)
265+
266+
thumbnail = result.get("thumbnail")
267+
if thumbnail:
268+
image_file = ImageFile(**thumbnail)
269+
result["thumbnail"] = await image_file.get_public_image_file_out(
270+
org, self.storage_ops
271+
)
272+
273+
return PublicCollOut.from_dict(result)
274+
251275
async def list_collections(
252276
self,
253277
org: Organization,
@@ -825,7 +849,7 @@ async def get_collection_public_replay(
825849
org: Organization = Depends(org_public),
826850
):
827851
coll = await colls.get_collection_out(
828-
coll_id, org, resources=True, public_only=True
852+
coll_id, org, resources=True, public_or_unlisted_only=True
829853
)
830854
response.headers["Access-Control-Allow-Origin"] = "*"
831855
response.headers["Access-Control-Allow-Headers"] = "*"
@@ -920,6 +944,24 @@ async def get_org_public_collections(
920944
sort_direction=sortDirection,
921945
)
922946

947+
@app.get(
948+
"/public-collections/{org_slug}/collections/{coll_id}",
949+
tags=["collections"],
950+
response_model=PublicCollOut,
951+
)
952+
async def get_public_collection(
953+
org_slug: str,
954+
coll_id: UUID,
955+
):
956+
try:
957+
org = await colls.orgs.get_org_by_slug(org_slug)
958+
# pylint: disable=broad-exception-caught
959+
except Exception:
960+
# pylint: disable=raise-missing-from
961+
raise HTTPException(status_code=404, detail="collection_not_found")
962+
963+
return await colls.get_public_collection_out(coll_id, org)
964+
923965
@app.get(
924966
"/orgs/{oid}/collections/{coll_id}/urls",
925967
tags=["collections"],

backend/test/test_collections.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515
CAPTION = "Short caption"
1616
UPDATED_CAPTION = "Updated caption"
1717

18+
NON_PUBLIC_COLL_FIELDS = (
19+
"oid",
20+
"modified",
21+
"crawlCount",
22+
"pageCount",
23+
"totalSize",
24+
"tags",
25+
"access",
26+
"homeUrlPageId",
27+
)
28+
NON_PUBLIC_IMAGE_FIELDS = ("originalFilename", "userid", "userName", "created")
29+
30+
1831
_coll_id = None
1932
_second_coll_id = None
2033
_public_coll_id = None
@@ -1024,7 +1037,7 @@ def test_list_public_colls_home_url_thumbnail():
10241037
assert coll["dateEarliest"]
10251038
assert coll["dateLatest"]
10261039

1027-
for field in non_public_fields:
1040+
for field in NON_PUBLIC_COLL_FIELDS:
10281041
assert field not in coll
10291042

10301043
if coll["id"] == _public_coll_id:
@@ -1042,13 +1055,69 @@ def test_list_public_colls_home_url_thumbnail():
10421055
assert thumbnail["size"]
10431056
assert thumbnail["mime"]
10441057

1045-
for field in non_public_image_fields:
1058+
for field in NON_PUBLIC_IMAGE_FIELDS:
10461059
assert field not in thumbnail
10471060

10481061
if coll["id"] == _second_public_coll_id:
10491062
assert coll["description"]
10501063

10511064

1065+
def test_get_public_collection():
1066+
r = requests.get(
1067+
f"{API_PREFIX}/public-collections/{default_org_slug}/collections/{_public_coll_id}"
1068+
)
1069+
assert r.status_code == 200
1070+
coll = r.json()
1071+
1072+
assert coll["id"] == _public_coll_id
1073+
assert coll["name"]
1074+
assert coll["resources"]
1075+
assert coll["dateEarliest"]
1076+
assert coll["dateLatest"]
1077+
1078+
for field in NON_PUBLIC_COLL_FIELDS:
1079+
assert field not in coll
1080+
1081+
assert coll["caption"] == CAPTION
1082+
1083+
assert coll["homeUrl"]
1084+
assert coll["homeUrlTs"]
1085+
1086+
thumbnail = coll["thumbnail"]
1087+
assert thumbnail
1088+
1089+
assert thumbnail["name"]
1090+
assert thumbnail["path"]
1091+
assert thumbnail["hash"]
1092+
assert thumbnail["size"]
1093+
assert thumbnail["mime"]
1094+
1095+
for field in NON_PUBLIC_IMAGE_FIELDS:
1096+
assert field not in thumbnail
1097+
1098+
# Invalid org slug - don't reveal whether org exists or not, use
1099+
# same exception as if collection doesn't exist
1100+
r = requests.get(
1101+
f"{API_PREFIX}/public-collections/doesntexist/collections/{_public_coll_id}"
1102+
)
1103+
assert r.status_code == 404
1104+
assert r.json()["detail"] == "collection_not_found"
1105+
1106+
# Invalid collection id
1107+
r = requests.get(
1108+
f"{API_PREFIX}/public-collections/{default_org_slug}/collections/doesntexist"
1109+
)
1110+
assert r.status_code == 404
1111+
assert r.json()["detail"] == "collection_not_found"
1112+
1113+
# Collection isn't public
1114+
r = requests.get(
1115+
f"{API_PREFIX}/public-collections/{default_org_slug}/collections/{ _coll_id}"
1116+
)
1117+
assert r.status_code == 404
1118+
assert r.json()["detail"] == "collection_not_found"
1119+
1120+
10521121
def test_delete_thumbnail(crawler_auth_headers, default_org_id):
10531122
r = requests.delete(
10541123
f"{API_PREFIX}/orgs/{default_org_id}/collections/{_public_coll_id}/thumbnail",

0 commit comments

Comments
 (0)