feat: add component_types management functionality

This commit is contained in:
Tran Anh Tuan
2026-05-11 10:15:02 +07:00
parent 7c9a0d4670
commit 50564e9b78
14 changed files with 1468 additions and 1 deletions

View File

@@ -15,7 +15,8 @@ const (
API_GROUP_ROOM = "/rooms" API_GROUP_ROOM = "/rooms"
API_GROUP_CABINET = "/cabinets" API_GROUP_CABINET = "/cabinets"
API_GROUP_SHELF = "/shelves" API_GROUP_SHELF = "/shelves"
API_GROUP_CONTAINER = "/containers" API_GROUP_CONTAINER = "/containers"
API_GROUP_COMPONENT_TYPE = "/component-types"
) )
const ( const (

View File

@@ -0,0 +1,30 @@
-- name: GetComponentTypeByID :one
SELECT * FROM component_types
WHERE id = sqlc.arg(id);
-- name: ListComponentTypes :many
SELECT * FROM component_types
ORDER BY created_at DESC;
-- name: CreateComponentType :one
INSERT INTO component_types (name, description,metadata, created_at)
VALUES (
sqlc.arg(name),
sqlc.arg(description),
sqlc.arg(metadata),
sqlc.arg(created_at)
)
RETURNING *;
-- name: UpdateComponentType :one
UPDATE component_types
SET name = CASE WHEN sqlc.arg(name) = '' THEN name ELSE sqlc.arg(name) END,
description = coalesce(sqlc.arg(description), description),
metadata = coalesce(sqlc.arg(metadata), metadata),
updated_at = sqlc.arg(updated_at)
WHERE id = sqlc.arg(id)
RETURNING *;
-- name: DeleteComponentType :execrows
DELETE FROM component_types
WHERE id = sqlc.arg(id);

View File

@@ -15,6 +15,279 @@ const docTemplate = `{
"host": "{{.Host}}", "host": "{{.Host}}",
"basePath": "{{.BasePath}}", "basePath": "{{.BasePath}}",
"paths": { "paths": {
"/api/v1/component-types": {
"get": {
"description": "Retrieve a list of all component types ordered by creation date",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "List all component types",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ComponentType"
}
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"post": {
"description": "Create a new component type with the provided details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Create a new component type",
"parameters": [
{
"description": "Component type request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CreateComponentTypeRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.CreateComponentTypeResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/api/v1/component-types/{id}": {
"get": {
"description": "Retrieve a single component type using its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Get component type by ID",
"parameters": [
{
"type": "integer",
"description": "Component type ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.ComponentType"
}
}
}
]
}
},
"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 component type by its ID. Only non-empty fields will be updated.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Update component type",
"parameters": [
{
"type": "integer",
"description": "Component type ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Component type request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateComponentTypeRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.UpdateComponentTypeResponse"
}
}
}
]
}
},
"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 component type by its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Delete component type",
"parameters": [
{
"type": "integer",
"description": "Component type 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"
}
}
}
}
},
"/auth/register": { "/auth/register": {
"post": { "post": {
"description": "Register with email, username and password", "description": "Register with email, username and password",
@@ -1489,6 +1762,32 @@ const docTemplate = `{
} }
} }
}, },
"models.ComponentType": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"models.Container": { "models.Container": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1637,6 +1936,26 @@ const docTemplate = `{
} }
} }
}, },
"requests.CreateComponentTypeRequest": {
"type": "object",
"required": [
"name"
],
"properties": {
"description": {
"type": "string"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
}
}
},
"requests.CreateContainerRequest": { "requests.CreateContainerRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -1737,6 +2056,23 @@ const docTemplate = `{
} }
} }
}, },
"requests.UpdateComponentTypeRequest": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
}
}
},
"requests.UpdateContainerRequest": { "requests.UpdateContainerRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1848,6 +2184,14 @@ const docTemplate = `{
} }
} }
}, },
"responses.CreateComponentTypeResponse": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"responses.CreateContainerResponse": { "responses.CreateContainerResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1897,6 +2241,20 @@ const docTemplate = `{
} }
} }
}, },
"responses.UpdateComponentTypeResponse": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"responses.UpdateContainerResponse": { "responses.UpdateContainerResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -9,6 +9,279 @@
"host": "localhost:3000", "host": "localhost:3000",
"basePath": "/api/v1", "basePath": "/api/v1",
"paths": { "paths": {
"/api/v1/component-types": {
"get": {
"description": "Retrieve a list of all component types ordered by creation date",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "List all component types",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ComponentType"
}
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
},
"post": {
"description": "Create a new component type with the provided details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Create a new component type",
"parameters": [
{
"description": "Component type request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CreateComponentTypeRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.CreateComponentTypeResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/api/v1/component-types/{id}": {
"get": {
"description": "Retrieve a single component type using its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Get component type by ID",
"parameters": [
{
"type": "integer",
"description": "Component type ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.ComponentType"
}
}
}
]
}
},
"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 component type by its ID. Only non-empty fields will be updated.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Update component type",
"parameters": [
{
"type": "integer",
"description": "Component type ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Component type request body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.UpdateComponentTypeRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.SuccessResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/responses.UpdateComponentTypeResponse"
}
}
}
]
}
},
"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 component type by its unique identifier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"component-type"
],
"summary": "Delete component type",
"parameters": [
{
"type": "integer",
"description": "Component type 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"
}
}
}
}
},
"/auth/register": { "/auth/register": {
"post": { "post": {
"description": "Register with email, username and password", "description": "Register with email, username and password",
@@ -1483,6 +1756,32 @@
} }
} }
}, },
"models.ComponentType": {
"type": "object",
"properties": {
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"models.Container": { "models.Container": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1631,6 +1930,26 @@
} }
} }
}, },
"requests.CreateComponentTypeRequest": {
"type": "object",
"required": [
"name"
],
"properties": {
"description": {
"type": "string"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
}
}
},
"requests.CreateContainerRequest": { "requests.CreateContainerRequest": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -1731,6 +2050,23 @@
} }
} }
}, },
"requests.UpdateComponentTypeRequest": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"metadata": {
"type": "array",
"items": {
"type": "integer"
}
},
"name": {
"type": "string"
}
}
},
"requests.UpdateContainerRequest": { "requests.UpdateContainerRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1842,6 +2178,14 @@
} }
} }
}, },
"responses.CreateComponentTypeResponse": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"responses.CreateContainerResponse": { "responses.CreateContainerResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1891,6 +2235,20 @@
} }
} }
}, },
"responses.UpdateComponentTypeResponse": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"responses.UpdateContainerResponse": { "responses.UpdateContainerResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -15,6 +15,23 @@ definitions:
updatedAt: updatedAt:
type: string type: string
type: object type: object
models.ComponentType:
properties:
createdAt:
type: string
description:
type: string
id:
type: integer
metadata:
items:
type: integer
type: array
name:
type: string
updatedAt:
type: string
type: object
models.Container: models.Container:
properties: properties:
containerType: containerType:
@@ -113,6 +130,19 @@ definitions:
- name - name
- roomId - roomId
type: object type: object
requests.CreateComponentTypeRequest:
properties:
description:
type: string
metadata:
items:
type: integer
type: array
name:
type: string
required:
- name
type: object
requests.CreateContainerRequest: requests.CreateContainerRequest:
properties: properties:
containerType: containerType:
@@ -180,6 +210,17 @@ definitions:
name: name:
type: string type: string
type: object type: object
requests.UpdateComponentTypeRequest:
properties:
description:
type: string
metadata:
items:
type: integer
type: array
name:
type: string
type: object
requests.UpdateContainerRequest: requests.UpdateContainerRequest:
properties: properties:
containerType: containerType:
@@ -252,6 +293,11 @@ definitions:
id: id:
type: integer type: integer
type: object type: object
responses.CreateComponentTypeResponse:
properties:
id:
type: integer
type: object
responses.CreateContainerResponse: responses.CreateContainerResponse:
properties: properties:
id: id:
@@ -283,6 +329,15 @@ definitions:
roomId: roomId:
type: integer type: integer
type: object type: object
responses.UpdateComponentTypeResponse:
properties:
description:
type: string
id:
type: integer
name:
type: string
type: object
responses.UpdateContainerResponse: responses.UpdateContainerResponse:
properties: properties:
containerType: containerType:
@@ -344,6 +399,176 @@ info:
title: Warehouse Management API title: Warehouse Management API
version: "1.0" version: "1.0"
paths: paths:
/api/v1/component-types:
get:
consumes:
- application/json
description: Retrieve a list of all component types ordered by creation date
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
items:
$ref: '#/definitions/models.ComponentType'
type: array
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: List all component types
tags:
- component-type
post:
consumes:
- application/json
description: Create a new component type with the provided details
parameters:
- description: Component type request body
in: body
name: body
required: true
schema:
$ref: '#/definitions/requests.CreateComponentTypeRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
$ref: '#/definitions/responses.CreateComponentTypeResponse'
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 component type
tags:
- component-type
/api/v1/component-types/{id}:
delete:
consumes:
- application/json
description: Delete a component type by its unique identifier
parameters:
- description: Component type 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 component type
tags:
- component-type
get:
consumes:
- application/json
description: Retrieve a single component type using its unique identifier
parameters:
- description: Component type 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.ComponentType'
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 component type by ID
tags:
- component-type
put:
consumes:
- application/json
description: Update an existing component type by its ID. Only non-empty fields
will be updated.
parameters:
- description: Component type ID
in: path
name: id
required: true
type: integer
- description: Component type request body
in: body
name: body
required: true
schema:
$ref: '#/definitions/requests.UpdateComponentTypeRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.SuccessResponse'
- properties:
data:
$ref: '#/definitions/responses.UpdateComponentTypeResponse'
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 component type
tags:
- component-type
/auth/register: /auth/register:
post: post:
consumes: consumes:

View File

@@ -0,0 +1,50 @@
package mapper
import (
"wm-backend/internal/models"
db "wm-backend/sqlc_gen"
"encoding/json"
"github.com/jackc/pgx/v5/pgtype"
)
func ToDomainComponentType(r db.ComponentType) *models.ComponentType {
return &models.ComponentType{
ID: r.ID,
Name: r.Name,
Description: r.Description.String,
Metadata: json.RawMessage(r.Metadata),
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
}
}
func ToModelComponentType(r *models.ComponentType) *db.CreateComponentTypeParams {
return &db.CreateComponentTypeParams{
Name: r.Name,
Description: pgtype.Text{
String: r.Description,
Valid: r.Description != "",
},
Metadata: []byte(r.Metadata),
CreatedAt: r.CreatedAt,
}
}
func ToUpdateModelComponentType(r *models.ComponentType) *db.UpdateComponentTypeParams {
var metadata []byte
if len(r.Metadata) > 0 {
metadata = []byte(r.Metadata)
}
return &db.UpdateComponentTypeParams{
Name: r.Name,
Description: pgtype.Text{
String: r.Description,
Valid: r.Description != "",
},
Metadata: metadata,
UpdatedAt: r.UpdatedAt,
ID: r.ID,
}
}

View File

@@ -0,0 +1,15 @@
package models
import (
"encoding/json"
"time"
)
type ComponentType struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Metadata json.RawMessage `json:"metadata"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

View File

@@ -0,0 +1,15 @@
package requests
import "encoding/json"
type CreateComponentTypeRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Metadata json.RawMessage `json:"metadata"`
}
type UpdateComponentTypeRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Metadata json.RawMessage `json:"metadata"`
}

View File

@@ -0,0 +1,11 @@
package responses
type CreateComponentTypeResponse struct {
ID int64 `json:"id"`
}
type UpdateComponentTypeResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}

View File

@@ -0,0 +1,52 @@
package repositories
import (
"context"
"wm-backend/internal/mapper"
"wm-backend/internal/models"
db "wm-backend/sqlc_gen"
)
func CreateComponentType(ctx context.Context, queries *db.Queries, body models.ComponentType) (models.ComponentType, error) {
result, err := queries.CreateComponentType(ctx, *mapper.ToModelComponentType(&body))
if err != nil {
return models.ComponentType{}, err
}
return *mapper.ToDomainComponentType(result), nil
}
func GetComponentTypeByID(ctx context.Context, queries *db.Queries, id int64) (models.ComponentType, error) {
result, err := queries.GetComponentTypeByID(ctx, id)
if err != nil {
return models.ComponentType{}, err
}
return *mapper.ToDomainComponentType(result), nil
}
func ListComponentTypes(ctx context.Context, queries *db.Queries) ([]models.ComponentType, error) {
results, err := queries.ListComponentTypes(ctx)
if err != nil {
return nil, err
}
var items []models.ComponentType
for _, r := range results {
items = append(items, *mapper.ToDomainComponentType(r))
}
return items, nil
}
func UpdateComponentType(ctx context.Context, queries *db.Queries, body models.ComponentType) (models.ComponentType, error) {
result, err := queries.UpdateComponentType(ctx, *mapper.ToUpdateModelComponentType(&body))
if err != nil {
return models.ComponentType{}, err
}
return *mapper.ToDomainComponentType(result), nil
}
func DeleteComponentType(ctx context.Context, queries *db.Queries, id int64) (int64, error) {
rowsAffected, err := queries.DeleteComponentType(ctx, id)
if err != nil {
return rowsAffected, err
}
return rowsAffected, nil
}

View File

@@ -73,6 +73,15 @@ func NewRouter() *gin.Engine {
container.PUT("/:id", utils.AsyncHandler(services.ContainerUpdate)) container.PUT("/:id", utils.AsyncHandler(services.ContainerUpdate))
container.DELETE("/:id", utils.AsyncHandler(services.ContainerDelete)) container.DELETE("/:id", utils.AsyncHandler(services.ContainerDelete))
} }
componentType := v1.Group(constants.API_GROUP_COMPONENT_TYPE)
{
componentType.GET("", utils.AsyncHandler(services.ComponentTypeList))
componentType.GET("/:id", utils.AsyncHandler(services.ComponentTypeGetByID))
componentType.POST("", utils.AsyncHandler(services.ComponentTypeCreate))
componentType.PUT("/:id", utils.AsyncHandler(services.ComponentTypeUpdate))
componentType.DELETE("/:id", utils.AsyncHandler(services.ComponentTypeDelete))
}
} }
r.GET(constants.API_PATH_PING, services.PingHandler) r.GET(constants.API_PATH_PING, services.PingHandler)

View File

@@ -0,0 +1,186 @@
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"
)
// ComponentTypeCreate creates a new component type.
// It validates the request body and creates the component type in the database.
//
// @Summary Create a new component type
// @Description Create a new component type with the provided details
// @Tags component-type
// @Accept json
// @Produce json
// @Param body body requests.CreateComponentTypeRequest true "Component type request body"
// @Success 201 {object} response.SuccessResponse{data=responses.CreateComponentTypeResponse}
// @Failure 400 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /api/v1/component-types [post]
func ComponentTypeCreate(c *gin.Context) error {
requestBody := requests.CreateComponentTypeRequest{}
if helper.IsShouldBindJSON(c, &requestBody) {
return nil
}
componentTypeModel := &models.ComponentType{
Name: requestBody.Name,
Description: requestBody.Description,
Metadata: requestBody.Metadata,
CreatedAt: time.Now(),
}
componentType, err := repositories.CreateComponentType(c.Request.Context(), global.Queries, *componentTypeModel)
if err != nil {
response.InternalServerError(c, http.StatusInternalServerError, "Failed to create component type")
return nil
}
response.Created(c, "Component type created successfully", &responses.CreateComponentTypeResponse{
ID: componentType.ID,
})
return nil
}
// ComponentTypeGetByID retrieves a single component type by its ID.
//
// @Summary Get component type by ID
// @Description Retrieve a single component type using its unique identifier
// @Tags component-type
// @Accept json
// @Produce json
// @Param id path int true "Component type ID"
// @Success 200 {object} response.SuccessResponse{data=models.ComponentType}
// @Failure 400 {object} response.ErrorResponse
// @Failure 404 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /api/v1/component-types/{id} [get]
func ComponentTypeGetByID(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
}
componentType, err := repositories.GetComponentTypeByID(c.Request.Context(), global.Queries, id)
if err != nil {
response.NotFoundError(c, http.StatusNotFound, "Component type not found")
return nil
}
response.Ok(c, "Success", componentType)
return nil
}
// ComponentTypeList retrieves all component types.
//
// @Summary List all component types
// @Description Retrieve a list of all component types ordered by creation date
// @Tags component-type
// @Accept json
// @Produce json
// @Success 200 {object} response.SuccessResponse{data=[]models.ComponentType}
// @Failure 500 {object} response.ErrorResponse
// @Router /api/v1/component-types [get]
func ComponentTypeList(c *gin.Context) error {
componentTypes, err := repositories.ListComponentTypes(c.Request.Context(), global.Queries)
if err != nil {
response.InternalServerError(c, http.StatusInternalServerError, "Failed to list component types")
return nil
}
response.Ok(c, "Success", componentTypes)
return nil
}
// ComponentTypeUpdate updates an existing component type by its ID.
// It validates the request body, fetches the existing record,
// merges non-empty fields from the request, and updates the component type in the database.
//
// @Summary Update component type
// @Description Update an existing component type by its ID. Only non-empty fields will be updated.
// @Tags component-type
// @Accept json
// @Produce json
// @Param id path int true "Component type ID"
// @Param body body requests.UpdateComponentTypeRequest true "Component type request body"
// @Success 200 {object} response.SuccessResponse{data=responses.UpdateComponentTypeResponse}
// @Failure 400 {object} response.ErrorResponse
// @Failure 404 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /api/v1/component-types/{id} [put]
func ComponentTypeUpdate(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.UpdateComponentTypeRequest{}
if helper.IsShouldBindJSON(c, &requestBody) {
return nil
}
existing, err := repositories.GetComponentTypeByID(c.Request.Context(), global.Queries, id)
if err != nil {
response.NotFoundError(c, http.StatusNotFound, "Component type not found")
return nil
}
if requestBody.Name != "" {
existing.Name = requestBody.Name
}
if requestBody.Description != "" {
existing.Description = requestBody.Description
}
if len(requestBody.Metadata) > 0 {
existing.Metadata = requestBody.Metadata
}
existing.UpdatedAt = time.Now()
componentType, err := repositories.UpdateComponentType(c.Request.Context(), global.Queries, existing)
if err != nil {
response.InternalServerError(c, http.StatusInternalServerError, "Failed to update component type")
return nil
}
response.Ok(c, "Component type updated successfully", &responses.UpdateComponentTypeResponse{
ID: componentType.ID,
Name: componentType.Name,
Description: componentType.Description,
})
return nil
}
// ComponentTypeDelete deletes a component type by its ID.
//
// @Summary Delete component type
// @Description Delete a component type by its unique identifier
// @Tags component-type
// @Accept json
// @Produce json
// @Param id path int true "Component type ID"
// @Success 200 {object} response.SuccessResponse
// @Failure 400 {object} response.ErrorResponse
// @Failure 500 {object} response.ErrorResponse
// @Router /api/v1/component-types/{id} [delete]
func ComponentTypeDelete(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
}
rowsAffected, err := repositories.DeleteComponentType(c.Request.Context(), global.Queries, id)
if err != nil {
log.Error().Err(err).Msgf("Failed to delete component type with ID: %d", id)
response.InternalServerError(c, http.StatusInternalServerError, "Failed to delete component type")
return nil
}
if rowsAffected == 0 {
response.NotFoundError(c, http.StatusNotFound, "Component type not found")
return nil
}
response.Ok(c, "Delete Success", nil)
return nil
}

View File

@@ -0,0 +1,152 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: conponent_type.sql
package db
import (
"context"
"time"
"github.com/jackc/pgx/v5/pgtype"
)
const createComponentType = `-- name: CreateComponentType :one
INSERT INTO component_types (name, description,metadata, created_at)
VALUES (
$1,
$2,
$3,
$4
)
RETURNING id, name, description, metadata, created_at, updated_at
`
type CreateComponentTypeParams struct {
Name string `db:"name" json:"name"`
Description pgtype.Text `db:"description" json:"description"`
Metadata []byte `db:"metadata" json:"metadata"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
}
func (q *Queries) CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, error) {
row := q.db.QueryRow(ctx, createComponentType,
arg.Name,
arg.Description,
arg.Metadata,
arg.CreatedAt,
)
var i ComponentType
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteComponentType = `-- name: DeleteComponentType :execrows
DELETE FROM component_types
WHERE id = $1
`
func (q *Queries) DeleteComponentType(ctx context.Context, id int64) (int64, error) {
result, err := q.db.Exec(ctx, deleteComponentType, id)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const getComponentTypeByID = `-- name: GetComponentTypeByID :one
SELECT id, name, description, metadata, created_at, updated_at FROM component_types
WHERE id = $1
`
func (q *Queries) GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, error) {
row := q.db.QueryRow(ctx, getComponentTypeByID, id)
var i ComponentType
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listComponentTypes = `-- name: ListComponentTypes :many
SELECT id, name, description, metadata, created_at, updated_at FROM component_types
ORDER BY created_at DESC
`
func (q *Queries) ListComponentTypes(ctx context.Context) ([]ComponentType, error) {
rows, err := q.db.Query(ctx, listComponentTypes)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ComponentType
for rows.Next() {
var i ComponentType
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateComponentType = `-- name: UpdateComponentType :one
UPDATE component_types
SET name = CASE WHEN $1 = '' THEN name ELSE $1 END,
description = coalesce($2, description),
metadata = coalesce($3, metadata),
updated_at = $4
WHERE id = $5
RETURNING id, name, description, metadata, created_at, updated_at
`
type UpdateComponentTypeParams struct {
Name interface{} `db:"name" json:"name"`
Description pgtype.Text `db:"description" json:"description"`
Metadata []byte `db:"metadata" json:"metadata"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
ID int64 `db:"id" json:"id"`
}
func (q *Queries) UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, error) {
row := q.db.QueryRow(ctx, updateComponentType,
arg.Name,
arg.Description,
arg.Metadata,
arg.UpdatedAt,
arg.ID,
)
var i ComponentType
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@@ -14,6 +14,7 @@ type Querier interface {
AssignRoleToUser(ctx context.Context, arg AssignRoleToUserParams) (UserRole, error) AssignRoleToUser(ctx context.Context, arg AssignRoleToUserParams) (UserRole, error)
CountUsersByRoleID(ctx context.Context, roleID uuid.UUID) (int64, error) CountUsersByRoleID(ctx context.Context, roleID uuid.UUID) (int64, error)
CreateCabinet(ctx context.Context, arg CreateCabinetParams) (Cabinet, error) CreateCabinet(ctx context.Context, arg CreateCabinetParams) (Cabinet, error)
CreateComponentType(ctx context.Context, arg CreateComponentTypeParams) (ComponentType, error)
CreateContainer(ctx context.Context, arg CreateContainerParams) (Container, error) CreateContainer(ctx context.Context, arg CreateContainerParams) (Container, error)
CreateRole(ctx context.Context, arg CreateRoleParams) (Role, error) CreateRole(ctx context.Context, arg CreateRoleParams) (Role, error)
CreateRoom(ctx context.Context, arg CreateRoomParams) (Room, error) CreateRoom(ctx context.Context, arg CreateRoomParams) (Room, error)
@@ -21,12 +22,14 @@ type Querier interface {
CreateUser(ctx context.Context, arg CreateUserParams) (uuid.UUID, error) CreateUser(ctx context.Context, arg CreateUserParams) (uuid.UUID, error)
CreateWarehouse(ctx context.Context, arg CreateWarehouseParams) (Warehouse, error) CreateWarehouse(ctx context.Context, arg CreateWarehouseParams) (Warehouse, error)
DeleteCabinet(ctx context.Context, id int64) (int64, error) DeleteCabinet(ctx context.Context, id int64) (int64, error)
DeleteComponentType(ctx context.Context, id int64) (int64, error)
DeleteContainer(ctx context.Context, id int64) (int64, error) DeleteContainer(ctx context.Context, id int64) (int64, error)
DeleteRole(ctx context.Context, id uuid.UUID) (int64, error) DeleteRole(ctx context.Context, id uuid.UUID) (int64, error)
DeleteRoom(ctx context.Context, id int64) (int64, error) DeleteRoom(ctx context.Context, id int64) (int64, error)
DeleteShelve(ctx context.Context, id int64) (int64, error) DeleteShelve(ctx context.Context, id int64) (int64, error)
DeleteWarehouse(ctx context.Context, id int64) (int64, error) DeleteWarehouse(ctx context.Context, id int64) (int64, error)
GetCabinetByID(ctx context.Context, id int64) (Cabinet, error) GetCabinetByID(ctx context.Context, id int64) (Cabinet, error)
GetComponentTypeByID(ctx context.Context, id int64) (ComponentType, error)
GetContainerByID(ctx context.Context, id int64) (Container, error) GetContainerByID(ctx context.Context, id int64) (Container, error)
GetRoleByID(ctx context.Context, id uuid.UUID) (Role, error) GetRoleByID(ctx context.Context, id uuid.UUID) (Role, error)
GetRoomByID(ctx context.Context, id int64) (Room, error) GetRoomByID(ctx context.Context, id int64) (Room, error)
@@ -39,6 +42,7 @@ type Querier interface {
GetUserRolesByUserID(ctx context.Context, userID uuid.UUID) ([]GetUserRolesByUserIDRow, error) GetUserRolesByUserID(ctx context.Context, userID uuid.UUID) ([]GetUserRolesByUserIDRow, error)
GetWarehouseByID(ctx context.Context, id int64) (Warehouse, error) GetWarehouseByID(ctx context.Context, id int64) (Warehouse, error)
ListCabinets(ctx context.Context) ([]Cabinet, error) ListCabinets(ctx context.Context) ([]Cabinet, error)
ListComponentTypes(ctx context.Context) ([]ComponentType, error)
ListContainers(ctx context.Context) ([]Container, error) ListContainers(ctx context.Context) ([]Container, error)
ListRoles(ctx context.Context) ([]Role, error) ListRoles(ctx context.Context) ([]Role, error)
ListRooms(ctx context.Context) ([]Room, error) ListRooms(ctx context.Context) ([]Room, error)
@@ -47,6 +51,7 @@ type Querier interface {
RemoveAllRolesFromUser(ctx context.Context, userID uuid.UUID) error RemoveAllRolesFromUser(ctx context.Context, userID uuid.UUID) error
RemoveRoleFromUser(ctx context.Context, arg RemoveRoleFromUserParams) error RemoveRoleFromUser(ctx context.Context, arg RemoveRoleFromUserParams) error
UpdateCabinet(ctx context.Context, arg UpdateCabinetParams) (Cabinet, error) UpdateCabinet(ctx context.Context, arg UpdateCabinetParams) (Cabinet, error)
UpdateComponentType(ctx context.Context, arg UpdateComponentTypeParams) (ComponentType, error)
UpdateContainer(ctx context.Context, arg UpdateContainerParams) (Container, error) UpdateContainer(ctx context.Context, arg UpdateContainerParams) (Container, error)
UpdateRole(ctx context.Context, arg UpdateRoleParams) (Role, error) UpdateRole(ctx context.Context, arg UpdateRoleParams) (Role, error)
UpdateRoom(ctx context.Context, arg UpdateRoomParams) (Room, error) UpdateRoom(ctx context.Context, arg UpdateRoomParams) (Room, error)