From 2f65cdd49b362a10a645397f72be4c30f0ac2728 Mon Sep 17 00:00:00 2001 From: piyush-jaiswal Date: Wed, 1 Oct 2025 15:15:15 +0530 Subject: [PATCH 1/3] migrate subcategory endpoints to flask-smorest --- app/__init__.py | 3 + app/migrated_routes/subcategory.py | 339 +++++++++++++++++++++++++++++ app/routes.py | 285 +----------------------- app/schemas.py | 21 ++ 4 files changed, 364 insertions(+), 284 deletions(-) create mode 100644 app/migrated_routes/subcategory.py diff --git a/app/__init__.py b/app/__init__.py index 4580a26..0f4d626 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -13,7 +13,10 @@ def register_blueprints(): from app.migrated_routes.category import bp as category_bp + from app.migrated_routes.subcategory import bp as subcategory_bp + api.register_blueprint(category_bp, url_prefix="/categories") + api.register_blueprint(subcategory_bp, url_prefix="/subcategories") app = Flask(__name__) diff --git a/app/migrated_routes/subcategory.py b/app/migrated_routes/subcategory.py new file mode 100644 index 0000000..1d9f2a2 --- /dev/null +++ b/app/migrated_routes/subcategory.py @@ -0,0 +1,339 @@ +from flask.views import MethodView +from flask_jwt_extended import jwt_required +from flask_smorest import Blueprint, abort +from psycopg2.errors import UniqueViolation +from sqlalchemy import UniqueConstraint +from sqlalchemy.exc import IntegrityError + +from app import db +from app.models import ( + Category, + Product, + Subcategory, + category_subcategory, + subcategory_product, +) +from app.schemas import ( + CategoriesOut, + PaginationArgs, + ProductsOut, + SubcategoriesOut, + SubcategoryIn, + SubcategoryOut, +) + +bp = Blueprint("subcategory", __name__) + + +@bp.route("") +class SubcategoryCollection(MethodView): + init_every_request = False + + @staticmethod + def _get_name_unique_constraint(): + name_col = Subcategory.__table__.c.name + return next( + con + for con in Subcategory.__table__.constraints + if isinstance(con, UniqueConstraint) + and len(con.columns) == 1 + and con.columns.contains_column(name_col) + ) + + _NAME_UNIQUE_CONSTRAINT = _get_name_unique_constraint() + + @bp.response(200, SubcategoriesOut) + def get(self): + """ + Get All Subcategories + --- + tags: + - Subcategory + description: Get all subcategories. + responses: + 200: + description: A list of subcategories. + """ + return {"subcategories": Subcategory.query.all()} + + @jwt_required() + @bp.arguments(SubcategoryIn) + @bp.response(201, SubcategoryOut) + def post(self, data): + """ + Create Subcategory + --- + tags: + - Subcategory + description: Create a new subcategory. + security: + - access_token: [] + requestBody: + required: true + description: name - Name of the subcategory
categories - Array of category ids (optional)
products - Array of product ids (optional) + content: + application/json: + schema: + type: object + required: + - name + properties: + name: + type: string + categories: + type: array + items: + type: integer + products: + type: array + items: + type: integer + responses: + 201: + description: Subcategory created successfully. + 400: + description: Invalid input. + 500: + description: Error occurred. + """ + subcategory = Subcategory(name=data["name"]) + + if c_ids := data.get("categories"): + categories = Category.query.filter(Category.id.in_(c_ids)).all() + if len(categories) != len(c_ids): + abort(422, message="One or more categories not present") + subcategory.categories = categories + + if p_ids := data.get("products"): + products = Product.query.filter(Product.id.in_(p_ids)).all() + if len(products) != len(p_ids): + abort(422, message="One or more products not present") + subcategory.products = products + + try: + db.session.add(subcategory) + db.session.commit() + except IntegrityError as ie: + db.session.rollback() + if ( + isinstance(ie.orig, UniqueViolation) + and ie.orig.diag.constraint_name + == SubcategoryCollection._NAME_UNIQUE_CONSTRAINT.name + ): + abort(409, message="Subcategory with this name already exists") + raise + + return subcategory + + +@bp.route("/") +class SubcategoryById(MethodView): + init_every_request = False + + def _get(self, id): + return Subcategory.query.get_or_404(id) + + @bp.response(200, SubcategoryOut) + def get(self, id): + """ + Get Subcategory + --- + tags: + - Subcategory + description: Get a subcategory by ID. + parameters: + - in: path + name: id + required: true + type: integer + description: Subcategory ID + responses: + 200: + description: Subcategory retrieved successfully. + 404: + description: Subcategory not found. + """ + return self._get(id) + + @jwt_required() + @bp.arguments(SubcategoryIn(partial=("name",))) + @bp.response(200, SubcategoryOut) + def put(self, data, id): + """ + Update Subcategory + --- + tags: + - Subcategory + description: Update an existing subcategory. + security: + - access_token: [] + parameters: + - in: path + name: id + required: true + type: integer + description: Subcategory ID + requestBody: + required: true + description: name - Name of the subcategory (optional)
categories - Array of category ids (optional)
products - Array of product ids (optional) + content: + application/json: + schema: + type: object + properties: + name: + type: string + categories: + type: array + items: + type: integer + products: + type: array + items: + type: integer + responses: + 200: + description: Subcategory updated successfully. + 400: + description: Invalid input. + 404: + description: Subcategory not found. + 500: + description: Error occurred. + """ + subcategory = self._get(id) + if name := data.get("name"): + subcategory.name = name + + with db.session.no_autoflush: + if c_ids := data.get("categories"): + categories = Category.query.filter(Category.id.in_(c_ids)).all() + if len(categories) != len(c_ids): + abort(422, message="One or more categories not present") + subcategory.categories.extend(categories) + + if p_ids := data.get("products"): + products = Product.query.filter(Product.id.in_(p_ids)).all() + if len(products) != len(p_ids): + abort(422, message="One or more products not present") + subcategory.products.extend(products) + + try: + db.session.commit() + except IntegrityError as ie: + db.session.rollback() + if ( + isinstance(ie.orig, UniqueViolation) + and ie.orig.diag.constraint_name + == category_subcategory.primary_key.name + ): + abort(409, message="Subcategory and category already linked") + if ( + isinstance(ie.orig, UniqueViolation) + and ie.orig.diag.constraint_name == subcategory_product.primary_key.name + ): + abort(409, message="Subcategory and product already linked") + raise + + return subcategory + + @jwt_required() + @bp.response(204) + def delete(self, id): + """ + Delete Subcategory + --- + tags: + - Subcategory + description: Delete a subcategory by ID. + security: + - access_token: [] + parameters: + - in: path + name: id + required: true + type: integer + description: Subcategory ID + responses: + 204: + description: Subcategory deleted successfully. + 404: + description: Subcategory not found. + 500: + description: Error occurred. + """ + subcategory = self._get(id) + db.session.delete(subcategory) + db.session.commit() + + +@bp.route("//categories") +class SubcategoryCategories(MethodView): + init_every_request = False + + @bp.response(200, CategoriesOut) + def get(self, id): + """ + Get Categories related to a Subcategory. + --- + tags: + - Subcategory + description: Get Categories related to a Subcategory. + parameters: + - in: path + name: id + required: true + type: integer + description: Subcategory ID + responses: + 200: + description: Categories retrieved successfully. + 404: + description: Subcategory not found. + 500: + description: Error occurred. + """ + subcategory = Subcategory.query.get_or_404(id) + return {"categories": subcategory.categories} + + +@bp.route("//products") +class SubcategoryProducts(MethodView): + init_every_request = False + _PER_PAGE = 10 + + @bp.arguments(PaginationArgs, location="query", as_kwargs=True) + @bp.response(200, ProductsOut) + def get(self, id, page): + """ + Get Products within a Subcategory. + --- + tags: + - Subcategory + description: Get products for a subcategory. + parameters: + - in: path + name: id + required: true + type: integer + description: Subcategory ID + - in: query + name: page + type: integer + default: 1 + description: Page number + responses: + 200: + description: Products retrieved successfully. + 404: + description: Subcategory not found. + 500: + description: Error occurred. + """ + subcategory = Subcategory.query.get_or_404(id) + + products = subcategory.products.order_by(Product.id.asc()).paginate( + page=page, per_page=SubcategoryProducts._PER_PAGE, error_out=False + ) + + return {"products": products} diff --git a/app/routes.py b/app/routes.py index 1356f6e..3046135 100644 --- a/app/routes.py +++ b/app/routes.py @@ -4,7 +4,7 @@ from email_validator import EmailNotValidError from app import app, db -from app.models import Category, Subcategory, Product, User +from app.models import Subcategory, Product, User @app.route('/auth/register', methods=['POST']) @@ -135,289 +135,6 @@ def refresh(): return jsonify(access_token=access_token), 200 -@app.route('/subcategory/create', methods=['POST']) -@jwt_required() -def create_subcategory(): - """ - Create Subcategory - --- - tags: - - Subcategory - description: Create a new subcategory. - security: - - access_token: [] - requestBody: - required: true - description: name - Name of the subcategory
categories - Array of category ids (optional)
products - Array of product ids (optional) - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - categories: - type: array - items: - type: integer - products: - type: array - items: - type: integer - responses: - 201: - description: Subcategory created successfully. - 400: - description: Invalid input. - 500: - description: Error occurred. - """ - if not request.json: - abort(400) - - try: - subcategory = Subcategory( - name=request.json.get('name') - ) - c_ids = request.json.get('categories') - p_ids = request.json.get('products') - if c_ids is not None: - categories = Category.query.filter(Category.id.in_(c_ids)) - subcategory.categories.extend(categories) - if p_ids is not None: - products = Product.query.filter(Product.id.in_(p_ids)) - subcategory.products.extend(products) - db.session.add(subcategory) - db.session.commit() - return jsonify(subcategory.to_json()), 201 - except: - return "Error occured", 500 - - -@app.route('/subcategories', methods=['GET']) -def get_all_subcategories(): - """ - Get All Subcategories - --- - tags: - - Subcategory - description: Get all subcategories. - responses: - 200: - description: A list of subcategories. - """ - subcategories = Subcategory.query.order_by(Subcategory.name).all() - return jsonify({"subcategories": [subcategory.to_json() for subcategory in subcategories]}), 200 - - -@app.route('/subcategory/', methods=['GET']) -def get_subcategory(sc_id): - """ - Get Subcategory - --- - tags: - - Subcategory - description: Get a subcategory by ID. - parameters: - - in: path - name: sc_id - required: true - type: integer - description: Subcategory ID - responses: - 200: - description: Subcategory retrieved successfully. - 404: - description: Subcategory not found. - """ - subcategory = Subcategory.query.get(sc_id) - if subcategory is None: - abort(404) - return jsonify(subcategory.to_json()), 200 - - -@app.route('/subcategory//update', methods=['PUT']) -@jwt_required() -def update_subcategory(sc_id): - """ - Update Subcategory - --- - tags: - - Subcategory - description: Update an existing subcategory. - security: - - access_token: [] - parameters: - - in: path - name: sc_id - required: true - type: integer - description: Subcategory ID - requestBody: - required: true - description: name - Name of the subcategory (optional)
categories - Array of category ids (optional)
products - Array of product ids (optional) - content: - application/json: - schema: - type: object - properties: - name: - type: string - categories: - type: array - items: - type: integer - products: - type: array - items: - type: integer - responses: - 201: - description: Subcategory updated successfully. - 400: - description: Invalid input. - 404: - description: Subcategory not found. - 500: - description: Error occurred. - """ - if not request.json: - abort(400) - - subcategory = Subcategory.query.get(sc_id) - if subcategory is None: - abort(404) - try: - name = request.json.get('name') - c_ids = request.json.get('categories') - p_ids = request.json.get('products') - if name is not None: - subcategory.name = request.json.get('name') - if c_ids is not None: - categories = Category.query.filter(Category.id.in_(c_ids)) - subcategory.categories.extend(categories) - if p_ids is not None: - products = Product.query.filter(Product.id.in_(p_ids)) - subcategory.products.extend(products) - db.session.commit() - return jsonify(subcategory.to_json()), 201 - except: - return "Error occured", 500 - - -@app.route("/subcategory/", methods=["DELETE"]) -@jwt_required() -def delete_subcategory(sc_id): - """ - Delete Subcategory - --- - tags: - - Subcategory - description: Delete a subcategory by ID. - security: - - access_token: [] - parameters: - - in: path - name: sc_id - required: true - type: integer - description: Subcategory ID - responses: - 200: - description: Subcategory deleted successfully. - 404: - description: Subcategory not found. - 500: - description: Error occurred. - """ - subcategory = Subcategory.query.get(sc_id) - if subcategory is None: - abort(404) - try: - db.session.delete(subcategory) - db.session.commit() - return jsonify({'result': True}), 200 - except: - return "Error occured", 500 - - -@app.route('/subcategory//categories', methods=['GET']) -def get_subcategory_categories(sc_id): - """ - Get Categories related to a Subcategory. - --- - tags: - - Subcategory - description: Get Categories related to a Subcategory. - parameters: - - in: path - name: sc_id - required: true - type: integer - description: Subcategory ID - responses: - 200: - description: Categories retrieved successfully. - 404: - description: Subcategory not found. - 500: - description: Error occurred. - """ - subcategory = Subcategory.query.get(sc_id) - if subcategory is None: - abort(404) - - try: - return { - "categories": [c.to_json() for c in subcategory.categories] - }, 200 - except: - return "Error occured", 500 - - -@app.route('/subcategory//products', methods=['GET']) -def get_subcategory_products(sc_id): - """ - Get Products within a Subcategory. - --- - tags: - - Subcategory - description: Get products for a subcategory. - parameters: - - in: path - name: sc_id - required: true - type: integer - description: Subcategory ID - - in: query - name: page - type: integer - default: 1 - description: Page number - responses: - 200: - description: Products retrieved successfully. - 404: - description: Subcategory not found. - 500: - description: Error occurred. - """ - subcategory = Subcategory.query.get(sc_id) - if not subcategory: - abort(404) - - try: - page = request.args.get("page", default=1, type=int) - products = subcategory.products.order_by(Product.id.asc()).paginate(page=page, per_page=10, error_out=False) - return { - "products": [p.to_json() for p in products] - }, 200 - except: - return "Error occured", 500 - - @app.route('/product/create', methods=['POST']) @jwt_required() def create_product(): diff --git a/app/schemas.py b/app/schemas.py index d8857a2..0272c43 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -42,6 +42,27 @@ class SubcategoriesOut(Schema): subcategories = fields.List(fields.Nested(SubcategoryOut)) +class SubcategoryIn(SQLAlchemySchema): + class Meta: + model = Subcategory + + name = auto_field() + categories = fields.List(fields.Int()) + products = fields.List(fields.Int()) + + @pre_load + def strip_strings(self, data, **kwargs): + if "name" in data: + data["name"] = data["name"].strip() + + return data + + @validates("name") + def validate_str_min_len(self, value, data_key): + if len(value) < 1: + raise ValidationError("Cannot be empty") + + class ProductOut(SQLAlchemyAutoSchema): class Meta: model = Product From 1756f82bfdb4c2f78ce6f0724a2fa9fdc0326e02 Mon Sep 17 00:00:00 2001 From: piyush-jaiswal Date: Wed, 1 Oct 2025 15:18:31 +0530 Subject: [PATCH 2/3] update subcategory tests and add duplicate link test --- tests/conftest.py | 2 +- tests/test_relationships.py | 81 +++++++++++++++++++++++++++---------- tests/test_subcategory.py | 40 ++++++++++-------- 3 files changed, 84 insertions(+), 39 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a913359..5a998a9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -73,7 +73,7 @@ def _create(name, categories=None, products=None, headers=None): payload["categories"] = categories if products is not None: payload["products"] = products - return client.post("/subcategory/create", json=payload, headers=headers) + return client.post("/subcategories", json=payload, headers=headers) return _create diff --git a/tests/test_relationships.py b/tests/test_relationships.py index 610b3e4..a2d0da2 100644 --- a/tests/test_relationships.py +++ b/tests/test_relationships.py @@ -112,23 +112,58 @@ def test_update_category_adds_linked_subcategories(self, create_authenticated_he assert self._category_subcategory_ids(category["id"]) == [subcategory["id"]] def test_update_subcategory_adds_categories_and_products(self, create_authenticated_headers, create_category, create_product, create_subcategory): - category1 = create_category("UC1").get_json() - category2 = create_category("UC2").get_json() - product1 = create_product("UP1").get_json() - product2 = create_product("UP2").get_json() - subcategory = create_subcategory("U_SC").get_json() - headers = create_authenticated_headers() + category1 = create_category("UC1", headers=headers).get_json() + category2 = create_category("UC2", headers=headers).get_json() + product1 = create_product("UP1", headers=headers).get_json() + product2 = create_product("UP2", headers=headers).get_json() + subcategory = create_subcategory("U_SC", headers=headers).get_json() + update_response = self.client.put( - f"/subcategory/{subcategory['id']}/update", + f"/subcategories/{subcategory['id']}", json={"categories": [category1["id"], category2["id"]], "products": [product1["id"], product2["id"]]}, headers=headers, ) - assert update_response.status_code == 201 + assert update_response.status_code == 200 assert self._subcategory_category_ids(subcategory["id"]) == sorted([category1["id"], category2["id"]]) assert self._subcategory_product_ids(subcategory["id"]) == sorted([product1["id"], product2["id"]]) + def test_update_subcategory_adds_linked_categories_and_products(self, create_authenticated_headers, create_category, create_product, create_subcategory): + headers = create_authenticated_headers() + category = create_category("UC1", headers=headers).get_json() + product = create_product("UP1", headers=headers).get_json() + subcategory = create_subcategory("U_SC", categories=[category["id"]], products=[product["id"]], headers=headers).get_json() + + with pytest.raises(IntegrityError) as ie_c: + self.client.put( + f"/subcategories/{subcategory['id']}", + json={"categories": [category["id"]]}, + headers=headers, + ) + with pytest.raises(IntegrityError) as ie_p: + self.client.put( + f"/subcategories/{subcategory['id']}", + json={"products": [product["id"]]}, + headers=headers, + ) + with pytest.raises(IntegrityError) as ie_cp: + self.client.put( + f"/subcategories/{subcategory['id']}", + json={"categories": [category["id"]], "products": [product["id"]]}, + headers=headers, + ) + + assert isinstance(ie_c.value.orig, sqlite3.IntegrityError) + assert isinstance(ie_p.value.orig, sqlite3.IntegrityError) + assert isinstance(ie_cp.value.orig, sqlite3.IntegrityError) + assert "UNIQUE constraint failed" in str(ie_c.value.orig) + assert "UNIQUE constraint failed" in str(ie_p.value.orig) + assert "UNIQUE constraint failed" in str(ie_cp.value.orig) + + assert self._subcategory_category_ids(subcategory["id"]) == [category["id"]] + assert self._subcategory_product_ids(subcategory["id"]) == [product["id"]] + def test_update_product_adds_subcategories(self, create_authenticated_headers, create_product, create_subcategory): subcategory1 = create_subcategory("UPS1").get_json() subcategory2 = create_subcategory("UPS2").get_json() @@ -177,32 +212,34 @@ def test_get_category_products_populated_with_pagination(self, create_category, def test_get_subcategory_categories_empty(self, create_subcategory): subcategory = create_subcategory("SC_NoCat").get_json() - resp = self.client.get(f"/subcategory/{subcategory['id']}/categories") + resp = self.client.get(f"/subcategories/{subcategory['id']}/categories") self._assert_related_collection(resp, "categories") - def test_get_subcategory_categories_populated(self, create_category, create_subcategory): - category1 = create_category("C1").get_json() - category2 = create_category("C2").get_json() - subcategory = create_subcategory("SC_Cats", categories=[category1["id"], category2["id"]]).get_json() + def test_get_subcategory_categories_populated(self, create_category, create_subcategory, create_authenticated_headers): + headers = create_authenticated_headers() + category1 = create_category("C1", headers=headers).get_json() + category2 = create_category("C2", headers=headers).get_json() + subcategory = create_subcategory("SC_Cats", categories=[category1["id"], category2["id"]], headers=headers).get_json() - resp = self.client.get(f"/subcategory/{subcategory['id']}/categories") + resp = self.client.get(f"/subcategories/{subcategory['id']}/categories") self._assert_related_collection(resp, "categories", expected_ids=[category1["id"], category2["id"]]) def test_get_subcategory_products_empty(self, create_subcategory): subcategory = create_subcategory("SC_NoProd").get_json() - resp = self.client.get(f"/subcategory/{subcategory['id']}/products") + resp = self.client.get(f"/subcategories/{subcategory['id']}/products") self._assert_related_collection(resp, "products") - def test_get_subcategory_products_populated_with_pagination(self, create_subcategory, create_product): - subcategory = create_subcategory("SC_Pag").get_json() + def test_get_subcategory_products_populated_with_pagination(self, create_subcategory, create_product, create_authenticated_headers): + headers = create_authenticated_headers() + subcategory = create_subcategory("SC_Pag", headers=headers).get_json() product_ids = set() for index in range(11): - product_resp = create_product(f"SP{index}", "desc", subcategories=[subcategory["id"]]) + product_resp = create_product(f"SP{index}", "desc", subcategories=[subcategory["id"]], headers=headers) product_ids.add(product_resp.get_json().get("id")) - page1 = self.client.get(f"/subcategory/{subcategory['id']}/products?page=1").get_json() - page2 = self.client.get(f"/subcategory/{subcategory['id']}/products?page=2").get_json() + page1 = self.client.get(f"/subcategories/{subcategory['id']}/products?page=1").get_json() + page2 = self.client.get(f"/subcategories/{subcategory['id']}/products?page=2").get_json() assert len(page1["products"]) == 10 assert len(page2["products"]) == 1 @@ -227,8 +264,8 @@ def test_get_product_subcategories_populated(self, create_product, create_subcat [ "/categories/999999/subcategories", "/categories/999999/products", - "/subcategory/999999/categories", - "/subcategory/999999/products", + "/subcategories/999999/categories", + "/subcategories/999999/products", "/product/999999/subcategories", ], ) diff --git a/tests/test_subcategory.py b/tests/test_subcategory.py index 4429dfb..651ba43 100644 --- a/tests/test_subcategory.py +++ b/tests/test_subcategory.py @@ -1,4 +1,7 @@ +import sqlite3 + import pytest +from sqlalchemy.exc import IntegrityError from app.models import Subcategory from tests import utils @@ -36,11 +39,15 @@ def test_create_subcategory(self, create_subcategory): assert "id" in data self._verify_subcategory_in_db(self.TEST_SUBCATEGORY_NAME) - def test_create_subcategory_duplicate_name(self, create_subcategory): - create_subcategory(self.TEST_SUBCATEGORY_NAME) - response = create_subcategory(self.TEST_SUBCATEGORY_NAME) + def test_create_subcategory_duplicate_name(self, create_subcategory, create_authenticated_headers): + headers = create_authenticated_headers() + create_subcategory(self.TEST_SUBCATEGORY_NAME, headers=headers) - assert response.status_code == 500 + with pytest.raises(IntegrityError) as ie: + create_subcategory(self.TEST_SUBCATEGORY_NAME, headers=headers) + + assert isinstance(ie.value.orig, sqlite3.IntegrityError) + assert "UNIQUE constraint failed" in str(ie.value.orig) assert self._count_subcategories() == 1 self._verify_subcategory_in_db(self.TEST_SUBCATEGORY_NAME) @@ -48,16 +55,17 @@ def test_get_subcategory_by_id(self, create_subcategory): response = create_subcategory("Laptops") data = response.get_json() sc_id = data["id"] - get_resp = self.client.get(f"/subcategory/{sc_id}") + get_resp = self.client.get(f"/subcategories/{sc_id}") assert get_resp.status_code == 200 data = get_resp.get_json() assert data["name"] == "Laptops" assert data["id"] == sc_id - def test_get_all_subcategories(self, create_subcategory): - create_subcategory("A") - create_subcategory("B") + def test_get_all_subcategories(self, create_subcategory, create_authenticated_headers): + headers = create_authenticated_headers() + create_subcategory("A", headers=headers) + create_subcategory("B", headers=headers) resp = self.client.get("/subcategories") assert resp.status_code == 200 @@ -74,10 +82,10 @@ def test_update_subcategory(self, create_authenticated_headers, create_subcatego data = response.get_json() sc_id = data["id"] update_resp = self.client.put( - f"/subcategory/{sc_id}/update", json={"name": "NewSubcat"}, headers=headers + f"/subcategories/{sc_id}", json={"name": "NewSubcat"}, headers=headers ) - assert update_resp.status_code == 201 + assert update_resp.status_code == 200 data = update_resp.get_json() assert data["name"] == "NewSubcat" assert data["id"] == sc_id @@ -89,10 +97,10 @@ def test_delete_subcategory(self, create_authenticated_headers, create_subcatego response = create_subcategory("ToDelete", headers=headers) data = response.get_json() sc_id = data["id"] - delete_resp = self.client.delete(f"/subcategory/{sc_id}", headers=headers) + delete_resp = self.client.delete(f"/subcategories/{sc_id}", headers=headers) - assert delete_resp.status_code == 200 - get_resp = self.client.get(f"/subcategory/{sc_id}") + assert delete_resp.status_code == 204 + get_resp = self.client.get(f"/subcategories/{sc_id}") assert get_resp.status_code == 404 self._verify_subcategory_in_db("ToDelete", should_exist=False) @@ -107,7 +115,7 @@ def test_delete_subcategory(self, create_authenticated_headers, create_subcatego def test_create_subcategory_token_error(self, get_headers, expected_code): headers = get_headers(self) response = self.client.post( - "/subcategory/create", json={"name": "CreateTokenError"}, headers=headers + "/subcategories", json={"name": "CreateTokenError"}, headers=headers ) utils.verify_token_error_response(response, expected_code) self._verify_subcategory_in_db("CreateTokenError", should_exist=False) @@ -128,7 +136,7 @@ def test_update_subcategory_token_error(self, get_headers, create_subcategory, c update_headers = get_headers(self) update_resp = self.client.put( - f"/subcategory/{sc_id}/update", + f"/subcategories/{sc_id}", json={"name": "UpdatedName"}, headers=update_headers, ) @@ -152,7 +160,7 @@ def test_delete_subcategory_token_error(self, get_headers, create_subcategory, c sc_id = data["id"] delete_headers = get_headers(self) - delete_resp = self.client.delete(f"/subcategory/{sc_id}", headers=delete_headers) + delete_resp = self.client.delete(f"/subcategories/{sc_id}", headers=delete_headers) utils.verify_token_error_response(delete_resp, expected_code) self._verify_subcategory_in_db("DeleteTokenError") From 56a82a50e3742df7a2a5e58326aef8720dc60fb5 Mon Sep 17 00:00:00 2001 From: piyush-jaiswal Date: Wed, 1 Oct 2025 15:19:07 +0530 Subject: [PATCH 3/3] update subcategory endpoints in README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8599e4a..14497d2 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Test the API using Swagger UI (`/` route), Postman, cURL or your preferred HTTP #### Fetch products using name, category, subcategory - [GET] `/product/` - Get product with name: `name`

-- [GET] `/subcategory//products?page=` - Get product with within subcategory `subcategory`. Returns `page_no` of the paginated results.

+- [GET] `/subcategories//products?page=` - Get product with within subcategory `subcategory`. Returns `page_no` of the paginated results.

- [GET] `/categories//products` - Get product with within category `category`. Returns first page of the paginated results.

- [GET] `/categories//products?page=` - Get product with within category `category`. Returns `page_no` of the paginated results.

@@ -124,11 +124,11 @@ Test the API using Swagger UI (`/` route), Postman, cURL or your preferred HTTP #### Subcategory - [GET] `/subcategories` - Get all subcategories -- [GET] `/subcategory/(int: subcategory_id)` - Get subcategory with subcategory_id -- [GET] `/subcategory/(int: subcategory_id)/categories` - Get categories related to subcategory_id -- [DELETE] `/subcategory/(int: subcategory_id)` (Protected) - Delete subcategory with subcategory_id +- [GET] `/subcategories/(int: subcategory_id)` - Get subcategory with subcategory_id +- [GET] `/subcategories/(int: subcategory_id)/categories` - Get categories related to subcategory_id +- [DELETE] `/subcategories/(int: subcategory_id)` (Protected) - Delete subcategory with subcategory_id -- [POST] `/subcategory/create` (Protected) - Create a new subcategory +- [POST] `/subcategories` (Protected) - Create a new subcategory ``` { "name": "name", @@ -137,7 +137,7 @@ Test the API using Swagger UI (`/` route), Postman, cURL or your preferred HTTP } ``` -- [PUT] `/subcategory/(int: subcategory_id)/update` (Protected) - Update subcategory with subcategory_id +- [PUT] `/subcategories/(int: subcategory_id)` (Protected) - Update subcategory with subcategory_id ``` { "name": "name",