diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 78c2dc4c8dfbc9a7d8e1258c458498af83298307..3fc17e890d63af8e156089091984b5f3147b3392 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -10,7 +10,7 @@ stages:
   - Static Analysis
   - Install
   - Test
-#  - Ship
+  - Ship
 
 # ------------------------------ Static analysis ------------------------------
 
@@ -64,15 +64,15 @@ Tests:
   script:
    - python tests/all.py
 
-## --------------------------------- Ship --------------------------------------
-#
-#pypi:
-#  stage: Ship
-#  only:
-#   - main
-#  before_script:
-#   - python3 -m pip install --upgrade build twine
-#  script:
-#   - python3 -m build
-#  after_script:
-#   - python3 -m twine upload --repository-url https://upload.pypi.org/legacy/ --non-interactive --verbose -u __token__ -p $pypi_token dist/*
+# --------------------------------- Ship --------------------------------------
+
+pypi:
+  stage: Ship
+  only:
+   - main
+  before_script:
+   - python3 -m pip install --upgrade build twine
+  script:
+   - python3 -m build
+  after_script:
+   - python3 -m twine upload --repository-url https://upload.pypi.org/legacy/ --non-interactive --verbose -u __token__ -p $pypi_token dist/*
diff --git a/tests/all.py b/tests/all.py
index 604c10664e86401c5b01f6f7e172a81b203717aa..cc75766611fe24d5f8d9b30000b1a7cd29233d01 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -19,7 +19,7 @@ image_href = (
     "otb/-/raw/develop/Data/Input/SP67_FR_subset_1.tif"
 )
 
-
+col_id = "collection-for-theia-dumper-tests"
 items_ids = [
     "item_1",
     "item_2"
@@ -33,6 +33,12 @@ with open(raster_file1, 'wb') as f:
 shutil.copyfile(raster_file1, raster_file2)
 
 
+def clear():
+    for item_id in items_ids:
+        handler.delete(col_id=col_id, item_id=item_id)
+    handler.delete(col_id=col_id)
+
+
 def create_item(item_id: str):
     """Create a STAC item."""
     item = pystac.Item(
@@ -66,7 +72,7 @@ def create_collection():
         intervals=[(None, None)]
     )
     col = pystac.Collection(
-        id="collection-for-theia-dumper-tests",
+        id=col_id,
         extent=pystac.Extent(spat_extent, temp_extent),
         description="Some description",
         href="http://hello.fr/collections/collection-for-tests",
@@ -117,23 +123,35 @@ def generate_item_collection(file_pth, relative=True):
 def test_item_collection():
     """Test item collection."""
     for relative in [True, False]:
+        print(f"Relative: {relative}")
+
+        # we need to create an empty collection before
+        col = create_collection()
+        handler.publish_collection(collection=col)
+
         with tempfile.NamedTemporaryFile() as tmp:
             generate_item_collection(tmp.name, relative=relative)
-            handler.publish(tmp.name)
+            handler.load_and_publish(tmp.name)
+            clear()
 
 
 def test_collection():
     """Test collection."""
     for relative in [True, False]:
+        print(f"\nRelative: {relative}")
         with tempfile.TemporaryDirectory() as tmpdir:
             generate_collection(tmpdir, relative=relative)
-            handler.publish(os.path.join(tmpdir, "collection.json"))
+            handler.load_and_publish(os.path.join(tmpdir, "collection.json"))
+            clear()
 
 
 def test_all():
     """Test all."""
-    test_item_collection()
+    # test collection
     test_collection()
 
+    # test item collection
+    test_item_collection()
+
 
 test_all()
diff --git a/theia_dumper/logger.py b/theia_dumper/logger.py
index 0f8c7a008407d3a9ef9ae5970f3fbce3dd4abf52..0021812bd9b44ae3fab4e4fa01e4e2c4472c0c77 100644
--- a/theia_dumper/logger.py
+++ b/theia_dumper/logger.py
@@ -1,3 +1,4 @@
+"""Logging stuff."""
 import logging
 logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
 logger = logging.getLogger(__name__)
diff --git a/theia_dumper/stac.py b/theia_dumper/stac.py
index 52b218748539e1f26642c9142147e0f720054a40..41a7809dc25bd3efcb35e5a542d5f709f937396b 100644
--- a/theia_dumper/stac.py
+++ b/theia_dumper/stac.py
@@ -1,14 +1,16 @@
+"""STAC stuff."""
+
 import pystac
-from pystac import Collection, ItemCollection, Item, Asset
+from pystac import Collection, ItemCollection, Item
+
 from .logger import logger
-from urllib.parse import urlparse, urljoin
-from typing import List, Any
+from urllib.parse import urljoin
+from typing import List
 import os
 import dinamis_sdk
 import requests
 from requests.adapters import HTTPAdapter, Retry
 from dataclasses import dataclass
-from functools import singledispatch
 
 
 class STACObjectUnresolved(Exception):
@@ -25,7 +27,7 @@ def create_session():
     retries = Retry(
         total=5,
         backoff_factor=1,
-        status_forcelist=[400, 401, 403, 408, 410, 419, 421, 422,
+        status_forcelist=[400, 403, 408, 410, 419, 421, 422,
                           424, 425, 429, 500, 502, 503, 504, 505],
         allowed_methods=frozenset(["PUT", "POST"])
     )
@@ -63,10 +65,11 @@ def load(obj_pth):
         "item collection": ItemCollection,
         "item": Item
     }.items():
+        logger.debug("Try to read file %s", obj_pth)
         try:
             obj = getattr(cls, "from_file")(obj_pth)
-            logger.info("Loaded %s", obj_name)
-            logger.info(obj.to_dict())
+            logger.info("Loaded %s from file %s", obj_name, obj_pth)
+            logger.debug(obj.to_dict())
             return obj
         except pystac.errors.STACTypeError:
             pass
@@ -76,11 +79,14 @@ def load(obj_pth):
 
 def get_assets_root_dir(items: List[Item]) -> str:
     """Get the common prefix of all items assets paths."""
-    return os.path.commonprefix([
+    prefix = os.path.commonprefix([
         asset.href
         for item in items
         for asset in item.assets.values()
     ])
+    if os.path.isdir(prefix):
+        return prefix
+    return os.path.dirname(prefix)
 
 
 def check_items_collection_id(items: List[Item]):
@@ -91,10 +97,23 @@ def check_items_collection_id(items: List[Item]):
         )
 
 
-def get_col_items(col: pystac.Collection) -> List[Item]:
+def get_col_href(col: Collection):
+    """Retrieve collection href."""
+    for link in col.links:
+        if link.rel == "self":
+            return link.href
+
+
+def get_col_items(col: Collection) -> List[Item]:
     """Retrieve collection items."""
+    col_href = get_col_href(col=col)
     return [
-        load(link.href)
+        load(
+            os.path.join(
+                os.path.dirname(col_href),
+                link.href[2:]
+            ) if link.href.startswith("./") else link.href
+        )
         for link in col.links
         if link.rel == "item"
     ]
@@ -108,13 +127,6 @@ class TransactionsHandler:
     storage_bucket: str
     assets_overwrite: bool
 
-    @singledispatch
-    def publish(self, obj: Any) -> Any:
-        """Publish a STAC object and its items/assets."""
-        raise TypeError(
-            "Invalid type, must be ItemCollection or Collection"
-        )
-
     def publish_item(self, item: Item, assets_root_dir: str):
         """Publish an item and all its assets"""
         col_id = item.collection_id
@@ -127,10 +139,12 @@ class TransactionsHandler:
 
         # Upload assets files
         for local_filename in local_assets_files:
+            logger.debug("Local file: %s", local_filename)
             target_url = local_filename.replace(
                 assets_root_dir,
                 target_root_dir
             )
+            logger.debug("Target file: %s", target_url)
 
             # Skip when target file exists and overwrite is not enabled
             if not self.assets_overwrite:
@@ -153,43 +167,79 @@ class TransactionsHandler:
             )
 
             # Update assets hrefs
+            logger.debug("Updating assets HREFs ...")
             for asset_name in item.assets:
                 item.assets[asset_name].href = target_url
 
             # Push item
+            logger.info(
+                "Publishing item \"%s\" in collection \"%s\"",
+                item.id,
+                col_id
+            )
             post_or_put(
                 urljoin(
                     self.stac_endpoint,
                     f"collections/{col_id}/items"
                 ),
-                item.to_dict()
+                item.to_dict(transform_hrefs=False)
             )
 
     def publish_items(self, items: List[Item]):
         """Publish items."""
         check_items_collection_id(items=items)
+        assets_root_dir = get_assets_root_dir(items=items)
+        logger.debug("Assets root directory: %s", assets_root_dir)
         for item in items:
             self.publish_item(
                 item=item,
-                assets_root_dir=get_assets_root_dir(items=items)
+                assets_root_dir=assets_root_dir
             )
 
-    @publish.register(Collection)
     def publish_collection(self, collection: Collection):
-        """Publish a collection and all its items."""
-        items = get_col_items(col=collection)
-        check_items_collection_id(items)
+        """Publish an empty collection"""
         post_or_put(
             url=urljoin(self.stac_endpoint, "/collections"),
             data=collection.to_dict()
         )
+
+    def publish_collection_with_items(self, collection: Collection):
+        """Publish a collection and all its items."""
+        items = get_col_items(col=collection)
+        check_items_collection_id(items)
+        self.publish_collection(collection=collection)
         self.publish_items(items=items)
 
-    @publish.register(ItemCollection)
     def publish_item_collection(self, item_collection: ItemCollection):
         """Publish an item collection and all of its items."""
         self.publish_items(items=item_collection.items)
 
     def load_and_publish(self, obj_pth: str):
         """Load and publish the serialized STAC object"""
-        self.publish(obj=load(obj_pth=obj_pth))
+        obj = load(obj_pth=obj_pth)
+        if isinstance(obj, Collection):
+            self.publish_collection_with_items(collection=obj)
+        elif isinstance(obj, ItemCollection):
+            self.publish_item_collection(item_collection=obj)
+        else:
+            raise TypeError(
+                "Invalid type, must be ItemCollection or Collection "
+                f"(got {type(obj)})"
+            )
+
+    def delete(self, col_id: str, item_id: str = None):
+        """Delete an item or a collection."""
+        logger.info("Delete item %s", item_id)
+        if item_id:
+            url = f"{self.stac_endpoint}/collections/{col_id}/items/{item_id}"
+        else:
+            url = f"{self.stac_endpoint}/collections/{col_id}"
+        resp = requests.delete(
+            url,
+            headers={
+                "Authorization":
+                    f"Bearer {dinamis_sdk.auth.get_access_token()}"
+            }
+        )
+        if resp.status_code != 200:
+            logger.warning("Deletion failed")