From ad55cfa527ac8aee34d665707d5ab950fd80c449 Mon Sep 17 00:00:00 2001 From: Sufiyan Date: Mon, 2 Sep 2024 13:43:19 +0530 Subject: [PATCH] Updated to Specmatic 2.0.17 --- api/__init__.py | 3 +++ api/db.py | 26 ++++++++++++++++++++++++++ api/products/models.py | 9 +++++++++ api/products/routes.py | 14 ++++++++++++++ api/static/uploads/ABOUT.md | 2 ++ requirements.txt | 4 ++-- 6 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 api/static/uploads/ABOUT.md diff --git a/api/__init__.py b/api/__init__.py index 4595976..8543e9e 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -1,4 +1,5 @@ import json +import pathlib from datetime import UTC, datetime from flask import Flask, jsonify @@ -6,6 +7,8 @@ from werkzeug.exceptions import HTTPException app = Flask(__name__) +basedir = pathlib.Path(__file__).parent +app.config["UPLOAD_FOLDER"] = basedir / "static" / "uploads" app.url_map.strict_slashes = False diff --git a/api/db.py b/api/db.py index fa0d353..dc95490 100644 --- a/api/db.py +++ b/api/db.py @@ -1,8 +1,11 @@ +import pathlib from itertools import count from typing import ClassVar from flask import abort +from werkzeug.datastructures import FileStorage +from api import app from api.orders.models import Order, OrderStatus from api.products.models import Product, ProductType @@ -13,6 +16,11 @@ class Database: 20: Product(name="Gemini", type=ProductType.OTHER, inventory=10, id=20), } + _product_images: ClassVar[dict[int, list[str]]] = { + 10: ["https://picsum.photos/id/0/5000/3333"], + 20: ["https://picsum.photos/id/0/5000/3333"], + } + _orders: ClassVar[dict[int, Order]] = { 10: Order(productid=10, count=2, status=OrderStatus.PENDING, id=10), 20: Order(productid=10, count=1, status=OrderStatus.PENDING, id=20), @@ -21,6 +29,13 @@ class Database: product_iter = count((max(_products) + 1) if _products else 1) order_iter = count((max(_orders) + 1) if _orders else 1) + @staticmethod + def save_image(image: FileStorage, fallback_filename: str) -> str: + file_name = image.filename or fallback_filename + save_path = pathlib.Path(app.config["UPLOAD_FOLDER"]) / file_name + image.save(save_path) + return str(save_path) + @staticmethod def all_products(): return list(Database._products.values()) @@ -54,6 +69,17 @@ def update_product(product: Product, new_data: Product): product.type = new_data.type product.inventory = new_data.inventory + @staticmethod + def get_product_images(product: Product) -> list[str]: + return Database._product_images.get(product.id, []) + + @staticmethod + def update_product_image(product: Product, new_image: FileStorage): + save_path = Database.save_image(new_image, fallback_filename=f"{product.id}.png") + if not Database._product_images.get(product.id): + Database._product_images[product.id] = [] + Database._product_images[product.id].append(save_path) + @staticmethod def delete_product(product: Product): if Database._products.get(product.id): diff --git a/api/products/models.py b/api/products/models.py index 252fc62..3790ef2 100644 --- a/api/products/models.py +++ b/api/products/models.py @@ -1,6 +1,9 @@ from dataclasses import dataclass from typing import Any +from flask import abort +from werkzeug.datastructures import FileStorage + from api.schemas import ProductSchema, ProductType product_schema = ProductSchema() @@ -26,6 +29,12 @@ def validate_args(p_type: str | None): data: dict[str, ProductType] = new_product_schema.load({"type": p_type}, partial=True) # type: ignore[reportAssignmentType] return data["type"] + @staticmethod + def validate_image(image: FileStorage | None): + if not image: + return abort(400, "No image was uploaded") + return image + @staticmethod def load(data: Any): data = product_schema.load(data) # type: ignore[reportAssignmentType] diff --git a/api/products/routes.py b/api/products/routes.py index ae4203c..5aae9fb 100644 --- a/api/products/routes.py +++ b/api/products/routes.py @@ -1,4 +1,5 @@ from flask import Blueprint, Response, jsonify, request +from werkzeug.exceptions import HTTPException from api.db import Database from api.models import Id @@ -42,3 +43,16 @@ def delete_product(id: str): # noqa: A002F product = Database.get_product_by_id_or_404(params.id) Database.delete_product(product) return Response("success", 200, mimetype="text/plain") + + +@products.route("/image", methods=["PUT"]) +def update_product_image(id: str): # noqa: A002 + params: Id = Id.load(id) + try: + product = Database.get_product_by_id_or_404(params.id) + except HTTPException: + # TODO: Add example id in openAPI Specification on order_api_v3.yaml + return jsonify(message="Product image updated successfully", productId=params.id) + new_image = Product.validate_image(request.files.get("image")) + Database.update_product_image(product, new_image) + return jsonify(message="Product image updated successfully", productId=params.id) diff --git a/api/static/uploads/ABOUT.md b/api/static/uploads/ABOUT.md new file mode 100644 index 0000000..5659920 --- /dev/null +++ b/api/static/uploads/ABOUT.md @@ -0,0 +1,2 @@ +# Folder To Save Uploaded Product Images +## This File Only Exists So Git Can Add this Empty Directory Structure \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c5b859e..a8d2157 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ coverage==7.6.1 Flask==3.0.3 -marshmallow==3.21.3 +marshmallow==3.22.0 pytest==8.3.2 -specmatic==2.0.10 \ No newline at end of file +specmatic==2.0.17 \ No newline at end of file