diff --git a/configs/constants/constants.go b/configs/constants/constants.go index f648bf1..bd11588 100644 --- a/configs/constants/constants.go +++ b/configs/constants/constants.go @@ -13,6 +13,7 @@ const ( API_GROUP_AUTH = "/auth" API_GROUP_WAREHOUSE = "/warehouses" API_GROUP_ROOM = "/rooms" + API_GROUP_CABINET = "/cabinets" ) const ( diff --git a/db/queries/cabinet.sql b/db/queries/cabinet.sql index cd21c0b..aea101e 100644 --- a/db/queries/cabinet.sql +++ b/db/queries/cabinet.sql @@ -18,7 +18,7 @@ RETURNING *; -- name: UpdateCabinet :one UPDATE cabinets -SET name = coalesce(sqlc.arg(name), name), +SET name = CASE WHEN sqlc.arg(name) = '' THEN name ELSE sqlc.arg(name) END, description = coalesce(sqlc.arg(description), description), updated_at = sqlc.arg(updated_at) WHERE id = sqlc.arg(id) diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 7b118f6..6df1cea 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -105,6 +105,279 @@ const docTemplate = `{ } } }, + "/v1/cabinets": { + "get": { + "description": "Retrieve a list of all cabinets ordered by creation date", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cabinet" + ], + "summary": "List all cabinets", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cabinet" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new cabinet with the provided details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cabinet" + ], + "summary": "Create a new cabinet", + "parameters": [ + { + "description": "Cabinet request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateCabinetRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateCabinetResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/cabinets/{id}": { + "get": { + "description": "Retrieve a single cabinet using its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cabinet" + ], + "summary": "Get cabinet by ID", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Cabinet" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update an existing cabinet by its ID. Only non-empty fields will be updated.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cabinet" + ], + "summary": "Update cabinet", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Cabinet request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateCabinetRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateCabinetResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a cabinet by its unique identifier", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cabinet" + ], + "summary": "Delete cabinet", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, "/v1/rooms": { "get": { "description": "Retrieve a list of all rooms ordered by creation date", @@ -647,6 +920,29 @@ const docTemplate = `{ } }, "definitions": { + "models.Cabinet": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, "models.Room": { "type": "object", "properties": { @@ -716,6 +1012,24 @@ const docTemplate = `{ } } }, + "requests.CreateCabinetRequest": { + "type": "object", + "required": [ + "name", + "roomId" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + } + } + }, "requests.CreateRoomRequest": { "type": "object", "required": [ @@ -752,6 +1066,17 @@ const docTemplate = `{ } } }, + "requests.UpdateCabinetRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "requests.UpdateRoomRequest": { "type": "object", "properties": { @@ -818,6 +1143,14 @@ const docTemplate = `{ } } }, + "responses.CreateCabinetResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "responses.CreateRoomResponse": { "type": "object", "properties": { @@ -834,6 +1167,23 @@ const docTemplate = `{ } } }, + "responses.UpdateCabinetResponse": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + } + } + }, "responses.UpdateRoomResponse": { "type": "object", "properties": { diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index c1d4dda..72f84a8 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -1,866 +1,1101 @@ { - "swagger": "2.0", - "info": { - "description": "This is the Warehouse Management API server.", - "title": "Warehouse Management API", - "contact": {}, - "version": "1.0" - }, - "host": "localhost:3000", - "basePath": "/api/v1", - "paths": { - "/auth/register": { - "post": { - "description": "Register with email, username and password", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "auth" - ], - "summary": "Register a new user", - "parameters": [ - { - "description": "Register request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/requests.BodyRegisterRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/responses.BodyRegisterResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "409": { - "description": "Conflict", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } + "swagger": "2.0", + "info": { + "description": "This is the Warehouse Management API server.", + "title": "Warehouse Management API", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:3000", + "basePath": "/api/v1", + "paths": { + "/auth/register": { + "post": { + "description": "Register with email, username and password", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["auth"], + "summary": "Register a new user", + "parameters": [ + { + "description": "Register request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.BodyRegisterRequest" } - }, - "/ping": { - "get": { - "description": "Check server is running", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "health" - ], - "summary": "Health check", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.BodyRegisterResponse" } + } } + ] } - }, - "/v1/rooms": { - "get": { - "description": "Retrieve a list of all rooms ordered by creation date", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "room" - ], - "summary": "List all rooms", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Room" - } - } - } - } - ] - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "post": { - "description": "Create a new room with the provided details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "room" - ], - "summary": "Create a new room", - "parameters": [ - { - "description": "Room request body", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/requests.CreateRoomRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/responses.CreateRoomResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" } - }, - "/v1/rooms/{id}": { - "get": { - "description": "Retrieve a single room using its unique identifier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "room" - ], - "summary": "Get room by ID", - "parameters": [ - { - "type": "integer", - "description": "Room ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.Room" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "put": { - "description": "Update an existing room by its ID. Only non-empty fields will be updated.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "room" - ], - "summary": "Update room", - "parameters": [ - { - "type": "integer", - "description": "Room ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Room request body", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/requests.UpdateRoomRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/responses.UpdateRoomResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Delete a room by its unique identifier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "room" - ], - "summary": "Delete room", - "parameters": [ - { - "type": "integer", - "description": "Room ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.SuccessResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" } - }, - "/v1/warehouses": { - "get": { - "description": "Retrieve a list of all warehouses ordered by creation date", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "warehouse" - ], - "summary": "List all warehouses", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Warehouse" - } - } - } - } - ] - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "post": { - "description": "Create a new warehouse with the provided details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "warehouse" - ], - "summary": "Create a new warehouse", - "parameters": [ - { - "description": "Warehouse request body", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/requests.CreateWarehouseRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/responses.CreateWarehouseResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - } - }, - "/v1/warehouses/{id}": { - "get": { - "description": "Retrieve a single warehouse using its unique identifier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "warehouse" - ], - "summary": "Get warehouse by ID", - "parameters": [ - { - "type": "integer", - "description": "Warehouse ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.Warehouse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "put": { - "description": "Update an existing warehouse by its ID with the provided details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "warehouse" - ], - "summary": "Update warehouse", - "parameters": [ - { - "type": "integer", - "description": "Warehouse ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Warehouse request body", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/requests.UpdateWarehouseRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.SuccessResponse" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/responses.UpdateWarehouseResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Delete a warehouse by its unique identifier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "warehouse" - ], - "summary": "Delete warehouse", - "parameters": [ - { - "type": "integer", - "description": "Warehouse ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.SuccessResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.ErrorResponse" - } - } - } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" } + } } + } }, - "definitions": { - "models.Room": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "warehouseId": { - "type": "integer" - } - } - }, - "models.Warehouse": { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - } - }, - "requests.BodyRegisterRequest": { - "type": "object", - "required": [ - "email", - "password", - "username" - ], - "properties": { - "email": { - "type": "string" - }, - "fullName": { - "type": "string" - }, - "password": { - "type": "string", - "minLength": 8 - }, - "username": { - "type": "string" - } - } - }, - "requests.CreateRoomRequest": { - "type": "object", - "required": [ - "name", - "warehouseId" - ], - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "warehouseId": { - "type": "integer" - } - } - }, - "requests.CreateWarehouseRequest": { - "type": "object", - "required": [ - "address", - "name" - ], - "properties": { - "address": { - "type": "string" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "requests.UpdateRoomRequest": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "requests.UpdateWarehouseRequest": { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, - "response.ErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "now": { - "type": "integer" - }, - "status": { - "type": "integer" - } - } - }, - "response.SuccessResponse": { - "type": "object", - "properties": { - "data": {}, - "message": { - "type": "string" - }, - "option": {}, - "reason_status_code": { - "type": "string" - }, - "status": { - "type": "integer" - } - } - }, - "responses.BodyRegisterResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - "responses.CreateRoomResponse": { - "type": "object", - "properties": { - "id": { - "type": "integer" - } - } - }, - "responses.CreateWarehouseResponse": { - "type": "object", - "properties": { - "id": { - "type": "integer" - } - } - }, - "responses.UpdateRoomResponse": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "warehouseId": { - "type": "integer" - } - } - }, - "responses.UpdateWarehouseResponse": { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } + "/ping": { + "get": { + "description": "Check server is running", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["health"], + "summary": "Health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } } + } } + } + }, + "/v1/cabinets": { + "get": { + "description": "Retrieve a list of all cabinets ordered by creation date", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["cabinet"], + "summary": "List all cabinets", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cabinet" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new cabinet with the provided details", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["cabinet"], + "summary": "Create a new cabinet", + "parameters": [ + { + "description": "Cabinet request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateCabinetRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateCabinetResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/cabinets/{id}": { + "get": { + "description": "Retrieve a single cabinet using its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["cabinet"], + "summary": "Get cabinet by ID", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Cabinet" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update an existing cabinet by its ID. Only non-empty fields will be updated.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["cabinet"], + "summary": "Update cabinet", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Cabinet request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateCabinetRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateCabinetResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a cabinet by its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["cabinet"], + "summary": "Delete cabinet", + "parameters": [ + { + "type": "integer", + "description": "Cabinet ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/rooms": { + "get": { + "description": "Retrieve a list of all rooms ordered by creation date", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["room"], + "summary": "List all rooms", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Room" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new room with the provided details", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["room"], + "summary": "Create a new room", + "parameters": [ + { + "description": "Room request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateRoomRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateRoomResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/rooms/{id}": { + "get": { + "description": "Retrieve a single room using its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["room"], + "summary": "Get room by ID", + "parameters": [ + { + "type": "integer", + "description": "Room ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Room" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update an existing room by its ID. Only non-empty fields will be updated.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["room"], + "summary": "Update room", + "parameters": [ + { + "type": "integer", + "description": "Room ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Room request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateRoomRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateRoomResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a room by its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["room"], + "summary": "Delete room", + "parameters": [ + { + "type": "integer", + "description": "Room ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/warehouses": { + "get": { + "description": "Retrieve a list of all warehouses ordered by creation date", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["warehouse"], + "summary": "List all warehouses", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Warehouse" + } + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "post": { + "description": "Create a new warehouse with the provided details", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["warehouse"], + "summary": "Create a new warehouse", + "parameters": [ + { + "description": "Warehouse request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CreateWarehouseRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CreateWarehouseResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } + }, + "/v1/warehouses/{id}": { + "get": { + "description": "Retrieve a single warehouse using its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["warehouse"], + "summary": "Get warehouse by ID", + "parameters": [ + { + "type": "integer", + "description": "Warehouse ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Warehouse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "put": { + "description": "Update an existing warehouse by its ID with the provided details", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["warehouse"], + "summary": "Update warehouse", + "parameters": [ + { + "type": "integer", + "description": "Warehouse ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Warehouse request body", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UpdateWarehouseRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.UpdateWarehouseResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Delete a warehouse by its unique identifier", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["warehouse"], + "summary": "Delete warehouse", + "parameters": [ + { + "type": "integer", + "description": "Warehouse ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.SuccessResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.ErrorResponse" + } + } + } + } } -} \ No newline at end of file + }, + "definitions": { + "models.Cabinet": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "models.Room": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "warehouseId": { + "type": "integer" + } + } + }, + "models.Warehouse": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "requests.BodyRegisterRequest": { + "type": "object", + "required": ["email", "password", "username"], + "properties": { + "email": { + "type": "string" + }, + "fullName": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 8 + }, + "username": { + "type": "string" + } + } + }, + "requests.CreateCabinetRequest": { + "type": "object", + "required": ["name", "roomId"], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + } + } + }, + "requests.CreateRoomRequest": { + "type": "object", + "required": ["name", "warehouseId"], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "warehouseId": { + "type": "integer" + } + } + }, + "requests.CreateWarehouseRequest": { + "type": "object", + "required": ["address", "name"], + "properties": { + "address": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "requests.UpdateCabinetRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "requests.UpdateRoomRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "requests.UpdateWarehouseRequest": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "response.ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "now": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "response.SuccessResponse": { + "type": "object", + "properties": { + "data": {}, + "message": { + "type": "string" + }, + "option": {}, + "reason_status_code": { + "type": "string" + }, + "status": { + "type": "integer" + } + } + }, + "responses.BodyRegisterResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "responses.CreateCabinetResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "responses.CreateRoomResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "responses.CreateWarehouseResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "responses.UpdateCabinetResponse": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "roomId": { + "type": "integer" + } + } + }, + "responses.UpdateRoomResponse": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "warehouseId": { + "type": "integer" + } + } + }, + "responses.UpdateWarehouseResponse": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + } + } +} diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 70df188..55eb4e5 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1,5 +1,20 @@ basePath: /api/v1 definitions: + models.Cabinet: + properties: + createdAt: + type: string + description: + type: string + id: + type: integer + name: + type: string + roomId: + type: integer + updatedAt: + type: string + type: object models.Room: properties: createdAt: @@ -46,6 +61,18 @@ definitions: - password - username type: object + requests.CreateCabinetRequest: + properties: + description: + type: string + name: + type: string + roomId: + type: integer + required: + - name + - roomId + type: object requests.CreateRoomRequest: properties: description: @@ -70,6 +97,13 @@ definitions: - address - name type: object + requests.UpdateCabinetRequest: + properties: + description: + type: string + name: + type: string + type: object requests.UpdateRoomRequest: properties: description: @@ -113,6 +147,11 @@ definitions: id: type: string type: object + responses.CreateCabinetResponse: + properties: + id: + type: integer + type: object responses.CreateRoomResponse: properties: id: @@ -123,6 +162,17 @@ definitions: id: type: integer type: object + responses.UpdateCabinetResponse: + properties: + description: + type: string + id: + type: integer + name: + type: string + roomId: + type: integer + type: object responses.UpdateRoomResponse: properties: description: @@ -208,6 +258,176 @@ paths: summary: Health check tags: - health + /v1/cabinets: + get: + consumes: + - application/json + description: Retrieve a list of all cabinets ordered by creation date + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + items: + $ref: '#/definitions/models.Cabinet' + type: array + type: object + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: List all cabinets + tags: + - cabinet + post: + consumes: + - application/json + description: Create a new cabinet with the provided details + parameters: + - description: Cabinet request body + in: body + name: body + required: true + schema: + $ref: '#/definitions/requests.CreateCabinetRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.CreateCabinetResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: Create a new cabinet + tags: + - cabinet + /v1/cabinets/{id}: + delete: + consumes: + - application/json + description: Delete a cabinet by its unique identifier + parameters: + - description: Cabinet ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.SuccessResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: Delete cabinet + tags: + - cabinet + get: + consumes: + - application/json + description: Retrieve a single cabinet using its unique identifier + parameters: + - description: Cabinet ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + $ref: '#/definitions/models.Cabinet' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: Get cabinet by ID + tags: + - cabinet + put: + consumes: + - application/json + description: Update an existing cabinet by its ID. Only non-empty fields will + be updated. + parameters: + - description: Cabinet ID + in: path + name: id + required: true + type: integer + - description: Cabinet request body + in: body + name: body + required: true + schema: + $ref: '#/definitions/requests.UpdateCabinetRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.UpdateCabinetResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.ErrorResponse' + summary: Update cabinet + tags: + - cabinet /v1/rooms: get: consumes: diff --git a/internal/mapper/cabinet_mapper.go b/internal/mapper/cabinet_mapper.go new file mode 100644 index 0000000..976de93 --- /dev/null +++ b/internal/mapper/cabinet_mapper.go @@ -0,0 +1,43 @@ +package mapper + +import ( + "wm-backend/internal/models" + db "wm-backend/sqlc_gen" + + "github.com/jackc/pgx/v5/pgtype" +) + +func ToDomainCabinet(r db.Cabinet) *models.Cabinet { + return &models.Cabinet{ + ID: r.ID, + RoomID: r.RoomID, + Name: r.Name, + Description: r.Description.String, + CreatedAt: r.CreatedAt, + UpdatedAt: r.UpdatedAt, + } +} + +func ToModelCabinet(r *models.Cabinet) *db.CreateCabinetParams { + return &db.CreateCabinetParams{ + RoomID: r.RoomID, + Name: r.Name, + Description: pgtype.Text{ + String: r.Description, + Valid: r.Description != "", + }, + CreatedAt: r.CreatedAt, + } +} + +func ToUpdateModelCabinet(r *models.Cabinet) *db.UpdateCabinetParams { + return &db.UpdateCabinetParams{ + Name: r.Name, + Description: pgtype.Text{ + String: r.Description, + Valid: r.Description != "", + }, + UpdatedAt: r.UpdatedAt, + ID: r.ID, + } +} diff --git a/internal/models/cabinet_model.go b/internal/models/cabinet_model.go new file mode 100644 index 0000000..70de6e6 --- /dev/null +++ b/internal/models/cabinet_model.go @@ -0,0 +1,12 @@ +package models + +import "time" + +type Cabinet struct { + ID int64 `json:"id"` + RoomID int64 `json:"roomId"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/internal/models/requests/cabinet_request.go b/internal/models/requests/cabinet_request.go new file mode 100644 index 0000000..5056a8a --- /dev/null +++ b/internal/models/requests/cabinet_request.go @@ -0,0 +1,12 @@ +package requests + +type CreateCabinetRequest struct { + RoomID int64 `json:"roomId" binding:"required"` + Name string `json:"name" binding:"required"` + Description string `json:"description"` +} + +type UpdateCabinetRequest struct { + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/internal/models/responses/cabinet_response.go b/internal/models/responses/cabinet_response.go new file mode 100644 index 0000000..ebe4155 --- /dev/null +++ b/internal/models/responses/cabinet_response.go @@ -0,0 +1,12 @@ +package responses + +type CreateCabinetResponse struct { + ID int64 `json:"id"` +} + +type UpdateCabinetResponse struct { + ID int64 `json:"id"` + RoomID int64 `json:"roomId"` + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/internal/repositories/cabinet_repository.go b/internal/repositories/cabinet_repository.go new file mode 100644 index 0000000..4752eed --- /dev/null +++ b/internal/repositories/cabinet_repository.go @@ -0,0 +1,48 @@ +package repositories + +import ( + "context" + "wm-backend/internal/mapper" + "wm-backend/internal/models" + db "wm-backend/sqlc_gen" +) + +func CreateCabinet(ctx context.Context, queries *db.Queries, body models.Cabinet) (models.Cabinet, error) { + result, err := queries.CreateCabinet(ctx, *mapper.ToModelCabinet(&body)) + if err != nil { + return models.Cabinet{}, err + } + return *mapper.ToDomainCabinet(result), nil +} + +func GetCabinetByID(ctx context.Context, queries *db.Queries, id int64) (models.Cabinet, error) { + result, err := queries.GetCabinetByID(ctx, id) + if err != nil { + return models.Cabinet{}, err + } + return *mapper.ToDomainCabinet(result), nil +} + +func ListCabinets(ctx context.Context, queries *db.Queries) ([]models.Cabinet, error) { + results, err := queries.ListCabinets(ctx) + if err != nil { + return nil, err + } + var items []models.Cabinet + for _, r := range results { + items = append(items, *mapper.ToDomainCabinet(r)) + } + return items, nil +} + +func UpdateCabinet(ctx context.Context, queries *db.Queries, body models.Cabinet) (models.Cabinet, error) { + result, err := queries.UpdateCabinet(ctx, *mapper.ToUpdateModelCabinet(&body)) + if err != nil { + return models.Cabinet{}, err + } + return *mapper.ToDomainCabinet(result), nil +} + +func DeleteCabinet(ctx context.Context, queries *db.Queries, id int64) error { + return queries.DeleteCabinet(ctx, id) +} diff --git a/internal/routers/router.go b/internal/routers/router.go index 0d5679c..615a3ef 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -46,6 +46,15 @@ func NewRouter() *gin.Engine { room.PUT("/:id", utils.AsyncHandler(services.RoomUpdate)) room.DELETE("/:id", utils.AsyncHandler(services.RoomDelete)) } + + cabinet := v1.Group(constants.API_GROUP_CABINET) + { + cabinet.GET("", utils.AsyncHandler(services.CabinetList)) + cabinet.GET("/:id", utils.AsyncHandler(services.CabinetGetByID)) + cabinet.POST("", utils.AsyncHandler(services.CabinetCreate)) + cabinet.PUT("/:id", utils.AsyncHandler(services.CabinetUpdate)) + cabinet.DELETE("/:id", utils.AsyncHandler(services.CabinetDelete)) + } } r.GET(constants.API_PATH_PING, services.PingHandler) diff --git a/internal/services/cabinet_service.go b/internal/services/cabinet_service.go new file mode 100644 index 0000000..e1446c4 --- /dev/null +++ b/internal/services/cabinet_service.go @@ -0,0 +1,181 @@ +package services + +import ( + "net/http" + "strconv" + "time" + "wm-backend/global" + "wm-backend/internal/models" + "wm-backend/internal/models/requests" + "wm-backend/internal/models/responses" + "wm-backend/internal/repositories" + "wm-backend/pkg/helper" + "wm-backend/response" + + "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" +) + +// CabinetCreate creates a new cabinet. +// It validates the request body and creates the cabinet in the database. +// +// @Summary Create a new cabinet +// @Description Create a new cabinet with the provided details +// @Tags cabinet +// @Accept json +// @Produce json +// @Param body body requests.CreateCabinetRequest true "Cabinet request body" +// @Success 201 {object} response.SuccessResponse{data=responses.CreateCabinetResponse} +// @Failure 400 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/cabinets [post] +func CabinetCreate(c *gin.Context) error { + requestBody := requests.CreateCabinetRequest{} + if helper.IsShouldBindJSON(c, &requestBody) { + return nil + } + cabinetModel := &models.Cabinet{ + RoomID: requestBody.RoomID, + Name: requestBody.Name, + Description: requestBody.Description, + CreatedAt: time.Now(), + } + cabinet, err := repositories.CreateCabinet(c.Request.Context(), global.Queries, *cabinetModel) + if err != nil { + log.Error().Err(err).Msg("Failed to create cabinet") + response.InternalServerError(c, http.StatusInternalServerError, "Failed to create cabinet") + return nil + } + response.Created(c, "Cabinet created successfully", &responses.CreateCabinetResponse{ + ID: cabinet.ID, + }) + return nil +} + +// CabinetGetByID retrieves a single cabinet by its ID. +// +// @Summary Get cabinet by ID +// @Description Retrieve a single cabinet using its unique identifier +// @Tags cabinet +// @Accept json +// @Produce json +// @Param id path int true "Cabinet ID" +// @Success 200 {object} response.SuccessResponse{data=models.Cabinet} +// @Failure 400 {object} response.ErrorResponse +// @Failure 404 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/cabinets/{id} [get] +func CabinetGetByID(c *gin.Context) error { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + response.BadRequestError(c, http.StatusBadRequest, "Invalid ID") + return nil + } + cabinet, err := repositories.GetCabinetByID(c.Request.Context(), global.Queries, id) + if err != nil { + log.Error().Err(err).Msgf("Failed to get cabinet by ID: %d", id) + response.NotFoundError(c, http.StatusNotFound, "Cabinet not found") + return nil + } + response.Ok(c, "Success", cabinet) + return nil +} + +// CabinetList retrieves all cabinets. +// +// @Summary List all cabinets +// @Description Retrieve a list of all cabinets ordered by creation date +// @Tags cabinet +// @Accept json +// @Produce json +// @Success 200 {object} response.SuccessResponse{data=[]models.Cabinet} +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/cabinets [get] +func CabinetList(c *gin.Context) error { + cabinets, err := repositories.ListCabinets(c.Request.Context(), global.Queries) + if err != nil { + response.InternalServerError(c, http.StatusInternalServerError, "Failed to list cabinets") + return nil + } + response.Ok(c, "Success", cabinets) + return nil +} + +// CabinetUpdate updates an existing cabinet by its ID. +// It validates the request body, fetches the existing record, +// merges non-empty fields from the request, and updates the cabinet in the database. +// +// @Summary Update cabinet +// @Description Update an existing cabinet by its ID. Only non-empty fields will be updated. +// @Tags cabinet +// @Accept json +// @Produce json +// @Param id path int true "Cabinet ID" +// @Param body body requests.UpdateCabinetRequest true "Cabinet request body" +// @Success 200 {object} response.SuccessResponse{data=responses.UpdateCabinetResponse} +// @Failure 400 {object} response.ErrorResponse +// @Failure 404 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/cabinets/{id} [put] +func CabinetUpdate(c *gin.Context) error { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + response.BadRequestError(c, http.StatusBadRequest, "Invalid ID") + return nil + } + requestBody := requests.UpdateCabinetRequest{} + if helper.IsShouldBindJSON(c, &requestBody) { + return nil + } + existing, err := repositories.GetCabinetByID(c.Request.Context(), global.Queries, id) + if err != nil { + response.NotFoundError(c, http.StatusNotFound, "Cabinet not found") + return nil + } + if requestBody.Name != "" { + existing.Name = requestBody.Name + } + if requestBody.Description != "" { + existing.Description = requestBody.Description + } + existing.UpdatedAt = time.Now() + cabinet, err := repositories.UpdateCabinet(c.Request.Context(), global.Queries, existing) + if err != nil { + response.InternalServerError(c, http.StatusInternalServerError, "Failed to update cabinet") + return nil + } + response.Ok(c, "Cabinet updated successfully", &responses.UpdateCabinetResponse{ + ID: cabinet.ID, + RoomID: cabinet.RoomID, + Name: cabinet.Name, + Description: cabinet.Description, + }) + return nil +} + +// CabinetDelete deletes a cabinet by its ID. +// +// @Summary Delete cabinet +// @Description Delete a cabinet by its unique identifier +// @Tags cabinet +// @Accept json +// @Produce json +// @Param id path int true "Cabinet ID" +// @Success 200 {object} response.SuccessResponse +// @Failure 400 {object} response.ErrorResponse +// @Failure 500 {object} response.ErrorResponse +// @Router /v1/cabinets/{id} [delete] +func CabinetDelete(c *gin.Context) error { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + response.BadRequestError(c, http.StatusBadRequest, "Invalid ID") + return nil + } + err = repositories.DeleteCabinet(c.Request.Context(), global.Queries, id) + if err != nil { + response.InternalServerError(c, http.StatusInternalServerError, "Failed to delete cabinet") + return nil + } + response.Ok(c, "Đã xóa thành công", nil) + return nil +} diff --git a/internal/services/room_service.go b/internal/services/room_service.go index ff353c2..223eabe 100644 --- a/internal/services/room_service.go +++ b/internal/services/room_service.go @@ -141,6 +141,7 @@ func RoomUpdate(c *gin.Context) error { existing.UpdatedAt = time.Now() room, err := repositories.UpdateRoom(c.Request.Context(), global.Queries, existing) if err != nil { + log.Error().Err(err).Msgf("Failed to update room with ID: %d", id) response.InternalServerError(c, http.StatusInternalServerError, "Failed to update room") return nil } @@ -173,6 +174,7 @@ func RoomDelete(c *gin.Context) error { } err = repositories.DeleteRoom(c.Request.Context(), global.Queries, id) if err != nil { + log.Error().Err(err).Msgf("Failed to delete room with ID: %d", id) response.InternalServerError(c, http.StatusInternalServerError, "Failed to delete room") return nil } diff --git a/sqlc_gen/cabinet.sql.go b/sqlc_gen/cabinet.sql.go index 0d84f53..1f41730 100644 --- a/sqlc_gen/cabinet.sql.go +++ b/sqlc_gen/cabinet.sql.go @@ -112,7 +112,7 @@ func (q *Queries) ListCabinets(ctx context.Context) ([]Cabinet, error) { const updateCabinet = `-- name: UpdateCabinet :one UPDATE cabinets -SET name = coalesce($1, name), +SET name = CASE WHEN $1 = '' THEN name ELSE $1 END, description = coalesce($2, description), updated_at = $3 WHERE id = $4 @@ -120,7 +120,7 @@ RETURNING id, room_id, name, description, created_at, updated_at ` type UpdateCabinetParams struct { - Name string `db:"name" json:"name"` + Name interface{} `db:"name" json:"name"` Description pgtype.Text `db:"description" json:"description"` UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` ID int64 `db:"id" json:"id"`