From 62754518e2f22b410323c98fee72e2363326ddf3 Mon Sep 17 00:00:00 2001 From: Charles Graham SWT Date: Tue, 30 Sep 2025 16:57:33 -0500 Subject: [PATCH 1/5] Add delete blob --- cwms/catalog/blobs.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cwms/catalog/blobs.py b/cwms/catalog/blobs.py index d94758c2..b69bb598 100644 --- a/cwms/catalog/blobs.py +++ b/cwms/catalog/blobs.py @@ -97,3 +97,23 @@ def store_blobs(data: JSON, fail_if_exists: Optional[bool] = True) -> None: endpoint = "blobs" params = {"fail-if-exists": fail_if_exists} return api.post(endpoint, data, params, api_version=1) + + +def delete_blob(blob_id: str, office_id: str) -> None: + """Delete a single BLOB. + + Parameters + ---------- + blob_id: string + Specifies the id of the blob. ALL blob ids are UPPERCASE. + office_id: string + Specifies the office of the blob. + + Returns + ------- + None + """ + + endpoint = f"blobs/{blob_id}" + params = {"office": office_id} + return api.delete(endpoint, params, api_version=1) From 2e834463aac632fd1e6c5c93330e2afe8a7485ad Mon Sep 17 00:00:00 2001 From: Charles Graham SWT Date: Tue, 30 Sep 2025 16:58:24 -0500 Subject: [PATCH 2/5] add patch blob --- cwms/catalog/blobs.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/cwms/catalog/blobs.py b/cwms/catalog/blobs.py index b69bb598..aff7b721 100644 --- a/cwms/catalog/blobs.py +++ b/cwms/catalog/blobs.py @@ -117,3 +117,42 @@ def delete_blob(blob_id: str, office_id: str) -> None: endpoint = f"blobs/{blob_id}" params = {"office": office_id} return api.delete(endpoint, params, api_version=1) + + +def update_blob(data: JSON, fail_if_not_exists: Optional[bool] = True) -> None: + f"""Update Existing Blob + + Parameters + ---------- + **Note**: The "id" field is automatically cast to uppercase. + + Data: JSON dictionary + JSON containing information of Blob to be updated. + + {STORE_DICT} + fail_if_not_exists: Boolean + Update will fail if the provided ID does not already exist. Default: True + + Returns + ------- + None + """ + + if not isinstance(data, JSON): + raise ValueError( + f"Cannot update a Blob without a JSON data dictionary:\n{STORE_DICT}" + ) + + if "id" not in data: + raise ValueError(f"Cannot update a Blob without an 'id' field:\n{STORE_DICT}") + + # Encode value if it's not already Base64-encoded + if "value" in data and not is_base64(data["value"]): + # Encode to bytes, then Base64, then decode to string for storing + data["value"] = base64.b64encode(data["value"].encode("utf-8")).decode("utf-8") + + blob_id = data.get("id", "").upper() + + endpoint = f"blobs/{blob_id}" + params = {"fail-if-not-exists": fail_if_not_exists} + return api.patch(endpoint, data, params, api_version=1) From 7260af45515c5fc32f1de29a6a7e1b4850871893 Mon Sep 17 00:00:00 2001 From: Charles Graham SWT Date: Tue, 30 Sep 2025 17:01:03 -0500 Subject: [PATCH 3/5] Correct type check to value check --- cwms/catalog/blobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwms/catalog/blobs.py b/cwms/catalog/blobs.py index aff7b721..7041a60d 100644 --- a/cwms/catalog/blobs.py +++ b/cwms/catalog/blobs.py @@ -138,7 +138,7 @@ def update_blob(data: JSON, fail_if_not_exists: Optional[bool] = True) -> None: None """ - if not isinstance(data, JSON): + if not data: raise ValueError( f"Cannot update a Blob without a JSON data dictionary:\n{STORE_DICT}" ) From 2feb63bbefcdb4bef737b59c1f2e11eba834d18a Mon Sep 17 00:00:00 2001 From: Charles Graham SWT Date: Mon, 6 Oct 2025 14:18:19 -0500 Subject: [PATCH 4/5] Correct pydoc formatting and test the update blob method --- cwms/catalog/blobs.py | 74 +++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/cwms/catalog/blobs.py b/cwms/catalog/blobs.py index 7041a60d..35b5b853 100644 --- a/cwms/catalog/blobs.py +++ b/cwms/catalog/blobs.py @@ -9,7 +9,7 @@ "office-id": "SWT", "id": "MYFILE_OR_BLOB_ID.TXT", "description": "Your description here", - "media-type-id": "application/octet-stream", + "media-type-id": "text/plain", "value": "STRING of content or BASE64_ENCODED_STRING" } """ @@ -19,16 +19,14 @@ def get_blob(blob_id: str, office_id: str) -> str: """Get a single BLOB (Binary Large Object). Parameters - ---------- - blob_id: string - Specifies the id of the blob. ALL blob ids are UPPERCASE. - office_id: string - Specifies the office of the blob. + blob_id: string + Specifies the id of the blob. ALL blob ids are UPPERCASE. + office_id: string + Specifies the office of the blob. - Returns - ------- - str: the value returned based on the content-type it was stored with as a string + Returns + str: the value returned based on the content-type it was stored with as a string """ endpoint = f"blobs/{blob_id}" @@ -44,18 +42,16 @@ def get_blobs( ) -> Data: """Get a subset of Blobs - Parameters - ---------- - office_id: Optional[string] - Specifies the office of the blob. - page_sie: Optional[Integer] - How many entries per page returned. Default 100. - blob_id_like: Optional[string] - Posix regular expression matching against the clob id - - Returns - ------- - cwms data type. data.json will return the JSON output and data.df will return a dataframe + Parameters: + office_id: Optional[string] + Specifies the office of the blob. + page_sie: Optional[Integer] + How many entries per page returned. Default 100. + blob_id_like: Optional[string] + Posix regular expression matching against the clob id + + Returns: + cwms data type. data.json will return the JSON output and data.df will return a dataframe """ endpoint = "blobs" @@ -66,21 +62,18 @@ def get_blobs( def store_blobs(data: JSON, fail_if_exists: Optional[bool] = True) -> None: - f"""Create New Blob + """Create New Blob - Parameters - ---------- + Parameters: **Note**: The "id" field is automatically cast to uppercase. Data: JSON dictionary JSON containing information of Blob to be updated. - {STORE_DICT} fail_if_exists: Boolean Create will fail if the provided ID already exists. Default: True - Returns - ------- + Returns: None """ @@ -103,15 +96,15 @@ def delete_blob(blob_id: str, office_id: str) -> None: """Delete a single BLOB. Parameters - ---------- - blob_id: string - Specifies the id of the blob. ALL blob ids are UPPERCASE. - office_id: string - Specifies the office of the blob. - - Returns - ------- - None + ---------- + blob_id: string + Specifies the id of the blob. ALL blob ids are UPPERCASE. + office_id: string + Specifies the office of the blob. + + Returns + ------- + None """ endpoint = f"blobs/{blob_id}" @@ -120,21 +113,18 @@ def delete_blob(blob_id: str, office_id: str) -> None: def update_blob(data: JSON, fail_if_not_exists: Optional[bool] = True) -> None: - f"""Update Existing Blob + """Update Existing Blob - Parameters - ---------- + Parameters: **Note**: The "id" field is automatically cast to uppercase. Data: JSON dictionary JSON containing information of Blob to be updated. - {STORE_DICT} fail_if_not_exists: Boolean Update will fail if the provided ID does not already exist. Default: True - Returns - ------- + Returns: None """ From 162c80cf19369925a94b45818e39733ef8cf4822 Mon Sep 17 00:00:00 2001 From: Charles Graham SWT Date: Mon, 6 Oct 2025 14:19:43 -0500 Subject: [PATCH 5/5] Bump patch version in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 65ea9030..ceeb6566 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "cwms-python" repository = "https://github.com/HydrologicEngineeringCenter/cwms-python" -version = "0.8.2" +version = "0.8.3" packages = [