s3.py

3.3 kB · python · 124 lines

1import boto32import json3import time4from botocore.exceptions import ClientError5from .api import delete_product, logger6from .config import CARLOMITCHENER_BUCKET, SITE_URL7from .errors import NoTaskError8from .models import Task910s3 = boto3.client("s3")1112BUCKET = CARLOMITCHENER_BUCKET13AUTOMATOR_KEY = "data/automator.json"14PATHS_KEY = "data/paths.json"15SITE_PREFIX = "site/"16CDN_PREFIX = f"{SITE_PREFIX}cdn/printful/"1718# KEYS1920def product_key(id: int) -> str:21    return f"data/products/{id}.json"2223def task_key(key: str) -> str:24    return f"data/tasks/{key}/{key}.json"2526def task_prefix(key: str) -> str:27    return f"data/tasks/{key}/"2829def cdn_prefix(key: str) -> str:30    return f"{CDN_PREFIX}{key}/"3132def cdn_key(key: str, name: str) -> str:33    return f"{CDN_PREFIX}{key}/{name}.png"3435def s3_url(key: str) -> str:36    if key.startswith(SITE_PREFIX):37        key = key[len(SITE_PREFIX):]38    return f"{SITE_URL}/{key}"3940# OBJECTS4142def put_json(key: str, data) -> None:43    s3.put_object(44        Bucket=BUCKET,45        Key=key,46        Body=json.dumps(data),47        ContentType="application/json",48    )4950def get_json(key: str) -> dict:51    try:52        response = s3.get_object(Bucket=BUCKET, Key=key)53    except ClientError as error:54        if error.response["Error"]["Code"] in ("NoSuchKey", "404"):55            raise NoTaskError(key)56        raise57    return json.loads(response["Body"].read().decode("utf-8"))5859def put_png(key: str, data) -> None:60    s3.upload_fileobj(data, Bucket=BUCKET, Key=key, ExtraArgs={"ContentType": "image/png"})6162def key_exists(prefix: str) -> bool:63    response = s3.list_objects_v2(Bucket=BUCKET, Prefix=prefix, MaxKeys=1)64    return "Contents" in response6566def list_objects(prefix: str) -> list[dict]:67    results = []68    paginator = s3.get_paginator("list_objects_v2")69    for page in paginator.paginate(Bucket=BUCKET, Prefix=prefix):70        results.extend(page.get("Contents", []))71    return results7273def delete_folder(prefix: str) -> int:74    objects = list_objects(prefix)75    for i in range(0, len(objects), 1000):76        batch = [{"Key": obj["Key"]} for obj in objects[i:i + 1000]]77        s3.delete_objects(Bucket=BUCKET, Delete={"Objects": batch})78    return len(objects)7980# CATALOG8182def task_exists(key: str) -> bool:83    return key_exists(task_key(key))8485def load_product(id: int) -> dict:86    return get_json(product_key(id))8788def load_paths() -> dict:89    return get_json(PATHS_KEY)9091def save_paths(data: dict) -> None:92    put_json(PATHS_KEY, data)9394# TASK9596def load_task() -> Task:97    data = get_json(AUTOMATOR_KEY)98    if not data:99        raise NoTaskError(AUTOMATOR_KEY)100    return Task.from_dict(data)101102def save_task(task: Task) -> None:103    task.updated_at = int(time.time())104    put_json(AUTOMATOR_KEY, task.to_dict())105106def archive_task(task: Task) -> None:107    put_json(task_key(task.key), task.to_dict())108109def clear_task() -> None:110    put_json(AUTOMATOR_KEY, {})111112def abort_task(task: Task) -> None:113    if task.product.shopify_id:114        try:115            delete_product(task)116        except Exception as error:117            logger.info(f"{task.desc} productDelete failed during abort: {error}")118    delete_folder(task_prefix(task.key))119    delete_folder(cdn_prefix(task.key))120    paths = load_paths()121    paths[str(task.product.id)] = None122    save_paths(paths)123    clear_task()124    logger.info(f"{task.desc} quarantined product {task.product.id}")