Skip to content

Commit e30a48c

Browse files
authored
52 add deleteupdate for blobs (#53)
Add the delete and update methods for Blob. Tested against localhost. Removed extra methods that were replaced when I learned about grouping commands.
1 parent ac32cc2 commit e30a48c

3 files changed

Lines changed: 138 additions & 104 deletions

File tree

cwmscli/commands/blob.py

Lines changed: 67 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def list_blobs(
228228
if limit is not None:
229229
df = df.head(limit)
230230

231-
logging.info(f"Found {len(df):,} blobs")
231+
logging.info(f"Found {len(df):,} blob(s)")
232232
# List the blobs in the logger
233233
for _, row in df.iterrows():
234234
logging.info(f"Blob ID: {row['id']}, Description: {row.get('description')}")
@@ -240,77 +240,6 @@ def get_media_type(file_path: str) -> str:
240240
return mime_type or "application/octet-stream"
241241

242242

243-
def main(
244-
directive: str,
245-
input_file: str,
246-
blob_id: str,
247-
description: Optional[str],
248-
media_type: Optional[str],
249-
office: str,
250-
api_root: str,
251-
api_key: str,
252-
overwrite: Optional[bool] = True,
253-
dry_run: Optional[bool] = False,
254-
):
255-
"""
256-
Upload, Download, Delete, or Update blob data in CWMS.
257-
258-
DIRECTIVE is the action to perform (upload, download, delete, update).
259-
INPUT_FILE is the path to the file on disk.
260-
BLOB_ID is the blob ID to store under.
261-
"""
262-
263-
cwms.api.init_session(api_root=api_root, api_key=api_key)
264-
file_data = None
265-
if directive in ["upload", "update"]:
266-
if not input_file or not os.path.isfile(input_file):
267-
logging.warning(
268-
"Valid input_file required for upload/update. Use --input-file to specify."
269-
)
270-
sys.exit(0)
271-
try:
272-
file_size = os.path.getsize(input_file)
273-
with open(input_file, "rb") as f:
274-
file_data = f.read()
275-
logging.info(f"Read file: {input_file} ({file_size} bytes)")
276-
except Exception as e:
277-
logging.error(f"Failed to read file: {e}")
278-
sys.exit(1)
279-
280-
# Determine what should be done based on directive
281-
if directive == "upload":
282-
store_blob(
283-
office=office,
284-
api_root=api_root,
285-
input_file=input_file,
286-
blob_id=blob_id,
287-
description=description,
288-
media_type=media_type,
289-
file_data=file_data,
290-
overwrite=overwrite,
291-
dry_run=dry_run,
292-
)
293-
elif directive == "list":
294-
list_blobs(office=office, blob_id_like=blob_id, sort_by="blob_id")
295-
elif directive == "download":
296-
retrieve_blob(
297-
office=office,
298-
blob_id=blob_id,
299-
)
300-
elif directive == "delete":
301-
# TODO: Delete endpoint does not exist in cwms-python yet
302-
logging.warning(
303-
"[NOT IMPLEMENTED] Delete Blob is not supported yet!\n\thttps://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
304-
)
305-
pass
306-
elif directive == "update":
307-
# TODO: Patch endpoint does not exist in cwms-python yet
308-
logging.warning(
309-
"[NOT IMPLEMENTED] Update Blob is not supported yet! Consider overwriting instead if a rename is not needed.\n\thttps://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
310-
)
311-
pass
312-
313-
314243
def upload_cmd(
315244
input_file: str,
316245
blob_id: str,
@@ -322,7 +251,7 @@ def upload_cmd(
322251
api_root: str,
323252
api_key: str,
324253
):
325-
cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
254+
cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
326255
try:
327256
file_size = os.path.getsize(input_file)
328257
with open(input_file, "rb") as f:
@@ -350,7 +279,7 @@ def upload_cmd(
350279
params = {"fail-if-exists": not overwrite}
351280

352281
if dry_run:
353-
logging.info(f"--dry-run: would POST {api_root}blobs with params={params}")
282+
logging.info(f"DRY RUN: would POST {api_root}blobs with params={params}")
354283
logging.info(
355284
json.dumps(
356285
{
@@ -376,8 +305,15 @@ def upload_cmd(
376305
sys.exit(1)
377306

378307

379-
def download_cmd(blob_id: str, dest: str, office: str, api_root: str, api_key: str):
380-
cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
308+
def download_cmd(
309+
blob_id: str, dest: str, office: str, api_root: str, api_key: str, dry_run: bool
310+
):
311+
if dry_run:
312+
logging.info(
313+
f"DRY RUN: would GET {api_root} blob with blob-id={blob_id} office={office}."
314+
)
315+
return
316+
cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, ""))
381317
bid = blob_id.upper()
382318
logging.debug(f"Office={office} BlobID={bid}")
383319

@@ -395,18 +331,62 @@ def download_cmd(blob_id: str, dest: str, office: str, api_root: str, api_key: s
395331
sys.exit(1)
396332

397333

398-
def delete_cmd(blob_id: str, office: str, api_root: str, api_key: str):
399-
logging.warning(
400-
"[NOT IMPLEMENTED] Delete Blob is not supported yet.\n"
401-
"See: https://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
402-
)
334+
def delete_cmd(blob_id: str, office: str, api_root: str, api_key: str, dry_run: bool):
403335

336+
if dry_run:
337+
logging.info(
338+
f"DRY RUN: would DELETE {api_root} blob with blob-id={blob_id} office={office}"
339+
)
340+
return
341+
cwms.init_session(api_root=api_root, api_key=api_key)
342+
cwms.delete_blob(office_id=office, blob_id=blob_id)
343+
logging.info(f"Deleted blob: {blob_id} for office: {office}")
404344

405-
def update_cmd(blob_id: str, input_file: str, office: str, api_root: str, api_key: str):
406-
logging.warning(
407-
"[NOT IMPLEMENTED] Update Blob is not supported yet. Consider --overwrite with upload.\n"
408-
"See: https://github.com/HydrologicEngineeringCenter/cwms-python/issues/192"
409-
)
345+
346+
def update_cmd(
347+
input_file: str,
348+
blob_id: str,
349+
description: str,
350+
media_type: str,
351+
overwrite: bool,
352+
dry_run: bool,
353+
office: str,
354+
api_root: str,
355+
api_key: str,
356+
):
357+
if dry_run:
358+
logging.info(
359+
f"DRY RUN: would PATCH {api_root} blob with blob-id={blob_id} office={office}"
360+
)
361+
return
362+
file_data = None
363+
if input_file:
364+
try:
365+
file_size = os.path.getsize(input_file)
366+
with open(input_file, "rb") as f:
367+
file_data = f.read()
368+
logging.info(f"Read file: {input_file} ({file_size} bytes)")
369+
except Exception as e:
370+
logging.error(f"Failed to read file: {e}")
371+
sys.exit(1)
372+
# Setup minimum required payload
373+
blob = {"office-id": office, "id": blob_id.upper()}
374+
if description:
375+
blob["description"] = description
376+
if media_type:
377+
blob["media-type-id"] = media_type
378+
else:
379+
logging.info("Media type not specified; Retrieving existing media type...")
380+
blob_metadata = cwms.get_blobs(office_id=office, blob_id_like=blob_id)
381+
blob["media-type-id"] = blob_metadata.df.get(
382+
"media-type-id", "application/octet-stream"
383+
)[0]
384+
logging.info(f"Using existing media type: {blob['media-type-id']}")
385+
386+
if file_data:
387+
blob["value"] = base64.b64encode(file_data).decode("utf-8")
388+
cwms.init_session(api_root=api_root, api_key=api_key)
389+
cwms.update_blob(blob, fail_if_not_exists=not overwrite)
410390

411391

412392
def list_cmd(
@@ -420,7 +400,7 @@ def list_cmd(
420400
api_root: str,
421401
api_key: str,
422402
):
423-
cwms.api.init_session(api_root=api_root, api_key=get_api_key(api_key, None))
403+
cwms.init_session(api_root=api_root, api_key=get_api_key(api_key, None))
424404
df = list_blobs(
425405
office=office,
426406
blob_id_like=blob_id_like,

cwmscli/commands/commands_cwms.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import textwrap
2+
13
import click
24

35
from cwmscli import requirements as reqs
@@ -94,12 +96,15 @@ def csv2cwms_cmd(**kwargs):
9496
@click.group(
9597
"blob",
9698
help="Manage CWMS Blobs (upload, download, delete, update, list)",
97-
epilog="""
98-
* Store a PDF/image as a CWMS blob with optional description
99-
* Download a blob by id to your local filesystem
100-
* Update a blob's name/description
101-
* Bulk list blobs for an office
102-
""",
99+
epilog=textwrap.dedent(
100+
"""
101+
Example Usage:\n
102+
- Store a PDF/image as a CWMS blob with optional description\n
103+
- Download a blob by id to your local filesystem\n
104+
- Update a blob's name/description/mime-type\n
105+
- Bulk list blobs for an office
106+
"""
107+
),
103108
)
104109
@requires(reqs.cwms)
105110
def blob_group():
@@ -148,6 +153,7 @@ def blob_upload(**kwargs):
148153
default=None,
149154
help="Destination file path. Defaults to blob-id.",
150155
)
156+
@click.option("--dry-run", is_flag=True, help="Show request; do not send.")
151157
@common_api_options
152158
def blob_download(**kwargs):
153159
from cwmscli.commands.blob import download_cmd
@@ -158,8 +164,9 @@ def blob_download(**kwargs):
158164
# ================================================================================
159165
# Delete
160166
# ================================================================================
161-
@blob_group.command("delete", help="[Not implemented] Delete a blob by ID")
167+
@blob_group.command("delete", help="Delete a blob by ID")
162168
@click.option("--blob-id", required=True, type=str, help="Blob ID to delete.")
169+
@click.option("--dry-run", is_flag=True, help="Show request; do not send.")
163170
@common_api_options
164171
def delete_cmd(**kwargs):
165172
from cwmscli.commands.blob import delete_cmd
@@ -170,14 +177,31 @@ def delete_cmd(**kwargs):
170177
# ================================================================================
171178
# Update
172179
# ================================================================================
173-
@blob_group.command("update", help="[Not implemented] Update/patch a blob by ID")
180+
@blob_group.command("update", help="Update/patch a blob by ID")
174181
@click.option("--blob-id", required=True, type=str, help="Blob ID to update.")
182+
@click.option("--dry-run", is_flag=True, help="Show request; do not send.")
183+
@click.option(
184+
"--description",
185+
default=None,
186+
help="New description JSON or text.",
187+
)
188+
@click.option(
189+
"--media-type",
190+
default=None,
191+
help="New media type (guessed from file if omitted).",
192+
)
175193
@click.option(
176194
"--input-file",
177195
required=False,
178196
type=click.Path(exists=True, dir_okay=False, readable=True, path_type=str),
179197
help="Optional file content to upload with update.",
180198
)
199+
@click.option(
200+
"--overwrite/--no-overwrite",
201+
default=False,
202+
show_default=True,
203+
help="If true, replace existing blob.",
204+
)
181205
@common_api_options
182206
def update_cmd(**kwargs):
183207
from cwmscli.commands.blob import update_cmd

poetry.lock

Lines changed: 39 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)