stac-fastapi-core 6.8.0__py3-none-any.whl → 6.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -126,7 +126,7 @@ class CatalogsExtension(ApiExtension):
126
126
  response_class=self.response_class,
127
127
  status_code=204,
128
128
  summary="Delete Catalog",
129
- description="Delete a catalog. Optionally cascade delete all collections in the catalog.",
129
+ description="Delete a catalog. All linked collections are unlinked and adopted by root if orphaned.",
130
130
  tags=["Catalogs"],
131
131
  )
132
132
 
@@ -337,22 +337,21 @@ class CatalogsExtension(ApiExtension):
337
337
  status_code=404, detail=f"Catalog {catalog_id} not found"
338
338
  )
339
339
 
340
- async def delete_catalog(
341
- self,
342
- catalog_id: str,
343
- request: Request,
344
- cascade: bool = Query(
345
- False,
346
- description="If true, delete all collections linked to this catalog. If false, only delete the catalog.",
347
- ),
348
- ) -> None:
349
- """Delete a catalog.
340
+ async def delete_catalog(self, catalog_id: str, request: Request) -> None:
341
+ """Delete a catalog (The Container).
342
+
343
+ Deletes the Catalog document itself. All linked Collections are unlinked
344
+ and adopted by Root if they become orphans. Collection data is NEVER deleted.
345
+
346
+ Logic:
347
+ 1. Finds all Collections linked to this Catalog.
348
+ 2. Unlinks them (removes catalog_id from their parent_ids).
349
+ 3. If a Collection becomes an orphan, it is adopted by Root.
350
+ 4. PERMANENTLY DELETES the Catalog document itself.
350
351
 
351
352
  Args:
352
353
  catalog_id: The ID of the catalog to delete.
353
354
  request: Request object.
354
- cascade: If true, delete all collections linked to this catalog.
355
- If false, only delete the catalog.
356
355
 
357
356
  Returns:
358
357
  None (204 No Content)
@@ -361,58 +360,42 @@ class CatalogsExtension(ApiExtension):
361
360
  HTTPException: If the catalog is not found.
362
361
  """
363
362
  try:
364
- # Get the catalog to verify it exists
363
+ # Verify the catalog exists
365
364
  await self.client.database.find_catalog(catalog_id)
366
365
 
367
- # Use reverse lookup query to find all collections with this catalog in parent_ids.
368
- # This is more reliable than parsing links, as it captures all collections
369
- # regardless of pagination or link truncation.
366
+ # Find all collections with this catalog in parent_ids
370
367
  query_body = {"query": {"term": {"parent_ids": catalog_id}}}
371
368
  search_result = await self.client.database.client.search(
372
369
  index=COLLECTIONS_INDEX, body=query_body, size=10000
373
370
  )
374
371
  children = [hit["_source"] for hit in search_result["hits"]["hits"]]
375
372
 
376
- # Process each child collection
373
+ # Safe Unlink: Remove catalog from all children's parent_ids
374
+ # If a child becomes an orphan, adopt it to root
375
+ root_id = self.settings.get("STAC_FASTAPI_LANDING_PAGE_ID", "stac-fastapi")
376
+
377
377
  for child in children:
378
378
  child_id = child.get("id")
379
379
  try:
380
- if cascade:
381
- # DANGER ZONE: User explicitly requested cascade delete.
382
- # Delete the collection entirely, regardless of other parents.
383
- await self.client.database.delete_collection(child_id)
384
- logger.info(
385
- f"Deleted collection {child_id} as part of cascade delete for catalog {catalog_id}"
386
- )
387
- else:
388
- # SAFE ZONE: Smart Unlink - Remove only this catalog from parent_ids.
389
- # The collection survives and becomes a root-level collection if it has no other parents.
390
- parent_ids = child.get("parent_ids", [])
391
- if catalog_id in parent_ids:
392
- parent_ids.remove(catalog_id)
393
- child["parent_ids"] = parent_ids
394
-
395
- # Update the collection in the database
396
- # Note: Catalog links are now dynamically generated, so no need to remove them
397
- await self.client.database.update_collection(
398
- collection_id=child_id,
399
- collection=child,
400
- refresh=False,
380
+ parent_ids = child.get("parent_ids", [])
381
+ if catalog_id in parent_ids:
382
+ parent_ids.remove(catalog_id)
383
+
384
+ # If orphan, move to root
385
+ if len(parent_ids) == 0:
386
+ parent_ids.append(root_id)
387
+ logger.info(
388
+ f"Collection {child_id} adopted by root after catalog deletion."
401
389
  )
402
-
403
- # Log the result
404
- if len(parent_ids) == 0:
405
- logger.info(
406
- f"Collection {child_id} is now a root-level orphan (no parent catalogs)"
407
- )
408
- else:
409
- logger.info(
410
- f"Removed catalog {catalog_id} from collection {child_id}; still belongs to {len(parent_ids)} other catalog(s)"
411
- )
412
390
  else:
413
- logger.debug(
414
- f"Catalog {catalog_id} not in parent_ids for collection {child_id}"
391
+ logger.info(
392
+ f"Removed catalog {catalog_id} from collection {child_id}; still belongs to {len(parent_ids)} other catalog(s)"
415
393
  )
394
+
395
+ child["parent_ids"] = parent_ids
396
+ await self.client.database.update_collection(
397
+ collection_id=child_id, collection=child, refresh=False
398
+ )
416
399
  except Exception as e:
417
400
  error_msg = str(e)
418
401
  if "not found" in error_msg.lower():
@@ -929,11 +912,11 @@ class CatalogsExtension(ApiExtension):
929
912
  async def delete_catalog_collection(
930
913
  self, catalog_id: str, collection_id: str, request: Request
931
914
  ) -> None:
932
- """Delete a collection from a catalog.
915
+ """Delete a collection from a catalog (Unlink only).
933
916
 
934
- If the collection has multiple parent catalogs, only removes this catalog
935
- from the parent_ids. If this is the only parent catalog, deletes the
936
- collection entirely.
917
+ Removes the catalog from the collection's parent_ids.
918
+ If the collection becomes an orphan (no parents), it is adopted by the Root.
919
+ It NEVER deletes the collection data.
937
920
 
938
921
  Args:
939
922
  catalog_id: The ID of the catalog.
@@ -959,37 +942,39 @@ class CatalogsExtension(ApiExtension):
959
942
  detail=f"Collection {collection_id} does not belong to catalog {catalog_id}",
960
943
  )
961
944
 
962
- # If the collection has multiple parents, just remove this catalog from parent_ids
963
- if len(parent_ids) > 1:
964
- parent_ids.remove(catalog_id)
965
- collection_db["parent_ids"] = parent_ids
945
+ # SAFE UNLINK LOGIC
946
+ parent_ids.remove(catalog_id)
966
947
 
967
- # Update the collection in the database
968
- # Note: Catalog links are now dynamically generated, so no need to remove them
969
- await self.client.database.update_collection(
970
- collection_id=collection_id, collection=collection_db, refresh=True
948
+ # Check if it is now an orphan (empty list)
949
+ if len(parent_ids) == 0:
950
+ # Fallback to Root / Landing Page
951
+ # You can hardcode 'root' or fetch the ID from settings
952
+ root_id = self.settings.get(
953
+ "STAC_FASTAPI_LANDING_PAGE_ID", "stac-fastapi"
971
954
  )
972
-
955
+ parent_ids.append(root_id)
973
956
  logger.info(
974
- f"Removed catalog {catalog_id} from collection {collection_id} parent_ids"
957
+ f"Collection {collection_id} unlinked from {catalog_id}. Orphaned, so adopted by root ({root_id})."
975
958
  )
976
959
  else:
977
- # If this is the only parent, delete the collection entirely
978
- await self.client.database.delete_collection(
979
- collection_id, refresh=True
980
- )
981
960
  logger.info(
982
- f"Deleted collection {collection_id} (only parent was catalog {catalog_id})"
961
+ f"Removed catalog {catalog_id} from collection {collection_id}; still belongs to {len(parent_ids)} other catalog(s)"
983
962
  )
984
963
 
964
+ # Update the collection in the database
965
+ collection_db["parent_ids"] = parent_ids
966
+ await self.client.database.update_collection(
967
+ collection_id=collection_id, collection=collection_db, refresh=True
968
+ )
969
+
985
970
  except HTTPException:
986
971
  raise
987
972
  except Exception as e:
988
973
  logger.error(
989
- f"Error deleting collection {collection_id} from catalog {catalog_id}: {e}",
974
+ f"Error removing collection {collection_id} from catalog {catalog_id}: {e}",
990
975
  exc_info=True,
991
976
  )
992
977
  raise HTTPException(
993
978
  status_code=500,
994
- detail=f"Failed to delete collection from catalog: {str(e)}",
979
+ detail=f"Failed to remove collection from catalog: {str(e)}",
995
980
  )
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "6.8.0"
2
+ __version__ = "6.8.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stac_fastapi_core
3
- Version: 6.8.0
3
+ Version: 6.8.1
4
4
  Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
5
5
  Project-URL: Homepage, https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -11,10 +11,10 @@ stac_fastapi/core/route_dependencies.py,sha256=hdtuMkv-zY1vg0YxiCz1aKP0SbBcORqDG
11
11
  stac_fastapi/core/serializers.py,sha256=l5EWZvlGjlfsZ3S4wHjWKD8sJBf83zd2dkursu18fV4,13345
12
12
  stac_fastapi/core/session.py,sha256=aXqu4LXfVbAAsChMVXd9gAhczA2bZPne6HqPeklAwMY,474
13
13
  stac_fastapi/core/utilities.py,sha256=XR_9afK_j8wCydgoXj-CMtRyI8KqgIL3d4HZOE779dU,7807
14
- stac_fastapi/core/version.py,sha256=ib-H9zXMsXhMlZkHut5gkqf27FPYcv0_wBkKUIXnJbA,45
14
+ stac_fastapi/core/version.py,sha256=mmv17WIhpgYJwiq4ovHUKIHbPTRN4ViHSjE63rArov0,45
15
15
  stac_fastapi/core/extensions/__init__.py,sha256=oaK-UJDQSEISdQ8VtM0ESxpsv7Hx1HbAdmMnh6MTFD4,356
16
16
  stac_fastapi/core/extensions/aggregation.py,sha256=v1hUHqlYuMqfQ554g3cTp16pUyRYucQxPERbHPAFtf8,1878
17
- stac_fastapi/core/extensions/catalogs.py,sha256=s6yrubil90fCrav5Xgg0bUiEeq7dTU6EPC3SBy055fQ,38935
17
+ stac_fastapi/core/extensions/catalogs.py,sha256=WwUI4Q20zbD5uN2le12uGnGPmQsoQnpD7bcJ__bi02Y,37963
18
18
  stac_fastapi/core/extensions/collections_search.py,sha256=xpv51nffMq5a8grNSaLbv2IzeI5JH_pqcoWRbWhzn6Y,14406
19
19
  stac_fastapi/core/extensions/fields.py,sha256=NCT5XHvfaf297eDPNaIFsIzvJnbbUTpScqF0otdx0NA,1066
20
20
  stac_fastapi/core/extensions/filter.py,sha256=-NQGME7rR_ereuDx-LAa1M5JhEXFaKiTtkH2asraYHE,2998
@@ -22,6 +22,6 @@ stac_fastapi/core/extensions/query.py,sha256=Xmo8pfZEZKPudZEjjozv3R0wLOP0ayjC9E6
22
22
  stac_fastapi/core/models/__init__.py,sha256=sUsEB7umGZVYXjT4EHqLwm8p2wevtRBdig2Ioj2ZdVQ,631
23
23
  stac_fastapi/core/models/links.py,sha256=5KEZKisFN34U4UuOzSQnDy0QdsUOT2VRuuY36vs-FGw,7074
24
24
  stac_fastapi/core/models/search.py,sha256=7SgAUyzHGXBXSqB4G6cwq9FMwoAS00momb7jvBkjyow,27
25
- stac_fastapi_core-6.8.0.dist-info/METADATA,sha256=OTeCtPe2s5cQCqoACfM5bddnUvL92ux6tU_jks6q5EU,3480
26
- stac_fastapi_core-6.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
- stac_fastapi_core-6.8.0.dist-info/RECORD,,
25
+ stac_fastapi_core-6.8.1.dist-info/METADATA,sha256=GB6GOulxa_ja31Bf1iRvzhCsGDPOVz44zBun5xuL_RE,3480
26
+ stac_fastapi_core-6.8.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
+ stac_fastapi_core-6.8.1.dist-info/RECORD,,