Compare commits
9 Commits
8adf1bd4c8
...
01a7f33054
| Author | SHA1 | Date | |
|---|---|---|---|
| 01a7f33054 | |||
| 92f13fba22 | |||
| 3b1b05d5a6 | |||
| 4a28259dc8 | |||
| af511203a4 | |||
| e06c30635e | |||
| ffdaa22aa3 | |||
| 1f4711edb6 | |||
| de58856289 |
40
cmd/server/main.go
Normal file
40
cmd/server/main.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
_ "gitea.gabilandia.com/gabdlr/agenda-web-go/docs"
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/database"
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/handler"
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/repository"
|
||||
httpSwagger "github.com/swaggo/http-swagger"
|
||||
)
|
||||
|
||||
// @title Contacts API
|
||||
// @version 1.0
|
||||
// @description A simple Contacts CRUD API
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.email support@yourapp.com
|
||||
|
||||
// @license.name MIT
|
||||
// @license.url https://opensource.org/licenses/MIT
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /
|
||||
func main() {
|
||||
err := database.InitDB(database.Conn_string)
|
||||
defer database.CloseDB()
|
||||
if err != nil {
|
||||
log.Fatal("Database connection failed:", err)
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
|
||||
contactRepo := repository.NewContactRepository(database.DB)
|
||||
handler.HandleContacts(mux, contactRepo)
|
||||
handler.HandlHealthChecks(mux)
|
||||
mux.HandleFunc("/swagger/", httpSwagger.WrapHandler)
|
||||
log.Fatal(http.ListenAndServe(":8080", mux))
|
||||
}
|
||||
380
docs/docs.go
Normal file
380
docs/docs.go
Normal file
@@ -0,0 +1,380 @@
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"email": "support@yourapp.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://opensource.org/licenses/MIT"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/contacts": {
|
||||
"get": {
|
||||
"description": "Get a list of all contacts",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Get all contacts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new contact with the provided data",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Create a new contact",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Contact object",
|
||||
"name": "contact",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/contacts/{id}": {
|
||||
"get": {
|
||||
"description": "Get a single contact by its ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Get a contact by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update an existing contact by ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Update a contact",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Contact object",
|
||||
"name": "contact",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a contact by ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Delete a contact",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"handler.APIError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "Error code",
|
||||
"type": "integer"
|
||||
},
|
||||
"details": {
|
||||
"description": "Additional error details",
|
||||
"type": "string"
|
||||
},
|
||||
"field": {
|
||||
"description": "Field name if applicable",
|
||||
"type": "string",
|
||||
"example": "name"
|
||||
},
|
||||
"message": {
|
||||
"description": "Human-readable error message",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.APIResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "The response data"
|
||||
},
|
||||
"errors": {
|
||||
"description": "List of errors if any occurred",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/handler.APIError"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"description": "Optional message",
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"description": "Indicates if the request was successful",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Contact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company": {
|
||||
"description": "Company the contact works for",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID is the unique identifier for the contact",
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the contact",
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"description": "Phone number in international format",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost:8080",
|
||||
BasePath: "/",
|
||||
Schemes: []string{},
|
||||
Title: "Contacts API",
|
||||
Description: "A simple Contacts CRUD API",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
356
docs/swagger.json
Normal file
356
docs/swagger.json
Normal file
@@ -0,0 +1,356 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "A simple Contacts CRUD API",
|
||||
"title": "Contacts API",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"email": "support@yourapp.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://opensource.org/licenses/MIT"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/",
|
||||
"paths": {
|
||||
"/contacts": {
|
||||
"get": {
|
||||
"description": "Get a list of all contacts",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Get all contacts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new contact with the provided data",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Create a new contact",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Contact object",
|
||||
"name": "contact",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/contacts/{id}": {
|
||||
"get": {
|
||||
"description": "Get a single contact by its ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Get a contact by ID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update an existing contact by ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Update a contact",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Contact object",
|
||||
"name": "contact",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.Contact"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a contact by ID",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"contacts"
|
||||
],
|
||||
"summary": "Delete a contact",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Contact ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"handler.APIError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "Error code",
|
||||
"type": "integer"
|
||||
},
|
||||
"details": {
|
||||
"description": "Additional error details",
|
||||
"type": "string"
|
||||
},
|
||||
"field": {
|
||||
"description": "Field name if applicable",
|
||||
"type": "string",
|
||||
"example": "name"
|
||||
},
|
||||
"message": {
|
||||
"description": "Human-readable error message",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.APIResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "The response data"
|
||||
},
|
||||
"errors": {
|
||||
"description": "List of errors if any occurred",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/handler.APIError"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"description": "Optional message",
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"description": "Indicates if the request was successful",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.Contact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"company": {
|
||||
"description": "Company the contact works for",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID is the unique identifier for the contact",
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the contact",
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"description": "Phone number in international format",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
231
docs/swagger.yaml
Normal file
231
docs/swagger.yaml
Normal file
@@ -0,0 +1,231 @@
|
||||
basePath: /
|
||||
definitions:
|
||||
handler.APIError:
|
||||
properties:
|
||||
code:
|
||||
description: Error code
|
||||
type: integer
|
||||
details:
|
||||
description: Additional error details
|
||||
type: string
|
||||
field:
|
||||
description: Field name if applicable
|
||||
example: name
|
||||
type: string
|
||||
message:
|
||||
description: Human-readable error message
|
||||
type: string
|
||||
type: object
|
||||
handler.APIResponse:
|
||||
properties:
|
||||
data:
|
||||
description: The response data
|
||||
errors:
|
||||
description: List of errors if any occurred
|
||||
items:
|
||||
$ref: '#/definitions/handler.APIError'
|
||||
type: array
|
||||
message:
|
||||
description: Optional message
|
||||
type: string
|
||||
success:
|
||||
description: Indicates if the request was successful
|
||||
type: boolean
|
||||
type: object
|
||||
models.Contact:
|
||||
properties:
|
||||
company:
|
||||
description: Company the contact works for
|
||||
type: string
|
||||
id:
|
||||
description: ID is the unique identifier for the contact
|
||||
type: integer
|
||||
name:
|
||||
description: Name of the contact
|
||||
type: string
|
||||
phone:
|
||||
description: Phone number in international format
|
||||
type: string
|
||||
type: object
|
||||
host: localhost:8080
|
||||
info:
|
||||
contact:
|
||||
email: support@yourapp.com
|
||||
name: API Support
|
||||
description: A simple Contacts CRUD API
|
||||
license:
|
||||
name: MIT
|
||||
url: https://opensource.org/licenses/MIT
|
||||
termsOfService: http://swagger.io/terms/
|
||||
title: Contacts API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/contacts:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get a list of all contacts
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.APIResponse'
|
||||
- properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/models.Contact'
|
||||
type: array
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
summary: Get all contacts
|
||||
tags:
|
||||
- contacts
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Create a new contact with the provided data
|
||||
parameters:
|
||||
- description: Contact object
|
||||
in: body
|
||||
name: contact
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.Contact'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.APIResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/models.Contact'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
summary: Create a new contact
|
||||
tags:
|
||||
- contacts
|
||||
/contacts/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Delete a contact by ID
|
||||
parameters:
|
||||
- description: Contact ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
summary: Delete a contact
|
||||
tags:
|
||||
- contacts
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get a single contact by its ID
|
||||
parameters:
|
||||
- description: Contact ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/handler.APIResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/models.Contact'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
summary: Get a contact by ID
|
||||
tags:
|
||||
- contacts
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Update an existing contact by ID
|
||||
parameters:
|
||||
- description: Contact ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: Contact object
|
||||
in: body
|
||||
name: contact
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.Contact'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.APIResponse'
|
||||
summary: Update a contact
|
||||
tags:
|
||||
- contacts
|
||||
swagger: "2.0"
|
||||
29
go.mod
29
go.mod
@@ -1,3 +1,32 @@
|
||||
module gitea.gabilandia.com/gabdlr/agenda-web-go
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.2 // indirect
|
||||
github.com/go-openapi/spec v0.22.0 // indirect
|
||||
github.com/go-openapi/swag v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.1 // indirect
|
||||
github.com/swaggo/files v1.0.1 // indirect
|
||||
github.com/swaggo/http-swagger v1.3.4 // indirect
|
||||
github.com/swaggo/swag v1.16.6 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
83
go.sum
Normal file
83
go.sum
Normal file
@@ -0,0 +1,83 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
|
||||
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
|
||||
github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
|
||||
github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
|
||||
github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw=
|
||||
github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0=
|
||||
github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8=
|
||||
github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo=
|
||||
github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0=
|
||||
github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo=
|
||||
github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw=
|
||||
github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc=
|
||||
github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw=
|
||||
github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg=
|
||||
github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA=
|
||||
github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
|
||||
github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
44
internal/database/database.go
Normal file
44
internal/database/database.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
var DB *sql.DB
|
||||
var dbName = os.Getenv("DB_NAME")
|
||||
var dbUser = os.Getenv("DB_USER")
|
||||
var dbPassword = os.Getenv("DB_PASSWORD")
|
||||
var dbHost = os.Getenv("DB_HOST")
|
||||
var dbPort = os.Getenv("DB_PORT")
|
||||
|
||||
var Conn_string = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci", dbUser, dbPassword, dbHost, dbPort, dbName)
|
||||
|
||||
func InitDB(dataSourceName string) error {
|
||||
var err error
|
||||
|
||||
DB, err = sql.Open("mysql", dataSourceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DB.SetMaxOpenConns(25)
|
||||
DB.SetMaxIdleConns(25)
|
||||
DB.SetConnMaxLifetime(5 * time.Minute)
|
||||
|
||||
if err = DB.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
println("DB connected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func CloseDB() {
|
||||
if DB != nil {
|
||||
DB.Close()
|
||||
}
|
||||
}
|
||||
47
internal/handler/base_handler.go
Normal file
47
internal/handler/base_handler.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Route struct {
|
||||
pattern string
|
||||
handler func(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type baseHandler struct {
|
||||
mux *http.ServeMux
|
||||
registeredRoutes map[string]bool
|
||||
Routes []Route
|
||||
}
|
||||
|
||||
var (
|
||||
bh *baseHandler
|
||||
bhOnce sync.Once
|
||||
bhMu sync.RWMutex
|
||||
)
|
||||
|
||||
func NewBaseHandler(mux *http.ServeMux, routes []Route) *baseHandler {
|
||||
bhOnce.Do(func() {
|
||||
bh = &baseHandler{
|
||||
mux: mux,
|
||||
registeredRoutes: make(map[string]bool),
|
||||
Routes: routes,
|
||||
}
|
||||
})
|
||||
|
||||
bhMu.Lock()
|
||||
defer bhMu.Unlock()
|
||||
bh.registerRoutes(routes)
|
||||
return bh
|
||||
}
|
||||
|
||||
func (b *baseHandler) registerRoutes(routes []Route) {
|
||||
for _, route := range routes {
|
||||
if !bh.registeredRoutes[route.pattern] {
|
||||
b.mux.HandleFunc(route.pattern, route.handler)
|
||||
bh.registeredRoutes[route.pattern] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
198
internal/handler/contact_handler.go
Normal file
198
internal/handler/contact_handler.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/models"
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/repository"
|
||||
)
|
||||
|
||||
type ContactHandler struct {
|
||||
repository repository.Repository[models.Contact]
|
||||
}
|
||||
|
||||
const required_field = "is required"
|
||||
const invalid_id = "Invalid ID"
|
||||
|
||||
func HandleContacts(mux *http.ServeMux, repo repository.Repository[models.Contact]) {
|
||||
ch := &ContactHandler{repository: repo}
|
||||
routes := []Route{
|
||||
{"GET /contacts", ch.getAll},
|
||||
{"GET /contacts/{id}", ch.getByID},
|
||||
{"POST /contacts", ch.create},
|
||||
{"PUT /contacts/{id}", ch.update},
|
||||
{"DELETE /contacts/{id}", ch.delete},
|
||||
}
|
||||
NewBaseHandler(mux, routes)
|
||||
}
|
||||
|
||||
// GetAll godoc
|
||||
// @Summary Get all contacts
|
||||
// @Description Get a list of all contacts
|
||||
// @Tags contacts
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} APIResponse{data=[]models.Contact}
|
||||
// @Failure 500 {object} APIResponse
|
||||
// @Router /contacts [get]
|
||||
func (h *ContactHandler) getAll(w http.ResponseWriter, r *http.Request) {
|
||||
contacts, err := h.repository.GetAll()
|
||||
if err != nil {
|
||||
InternalError(w, err)
|
||||
return
|
||||
}
|
||||
JSONSuccess(w, contacts, "Contact list retrieved successfully", http.StatusOK)
|
||||
}
|
||||
|
||||
// GetByID godoc
|
||||
// @Summary Get a contact by ID
|
||||
// @Description Get a single contact by its ID
|
||||
// @Tags contacts
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Contact ID"
|
||||
// @Success 200 {object} APIResponse{data=models.Contact}
|
||||
// @Failure 400 {object} APIResponse
|
||||
// @Failure 404 {object} APIResponse
|
||||
// @Failure 500 {object} APIResponse
|
||||
// @Router /contacts/{id} [get]
|
||||
func (h *ContactHandler) getByID(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.PathValue("id"))
|
||||
if err != nil {
|
||||
BadRequest(w, ErrInvalidFormat, invalid_id)
|
||||
return
|
||||
}
|
||||
|
||||
contact, err := h.repository.GetByID(id)
|
||||
if err != nil {
|
||||
InternalError(w, err)
|
||||
return
|
||||
}
|
||||
if contact == nil {
|
||||
NotFound(w, ErrNotFound, "Contact not found")
|
||||
return
|
||||
}
|
||||
JSONSuccess(w, contact, "Contact retrieved successfully", http.StatusOK)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
// @Summary Create a new contact
|
||||
// @Description Create a new contact with the provided data
|
||||
// @Tags contacts
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param contact body models.Contact true "Contact object"
|
||||
// @Success 201 {object} APIResponse{data=models.Contact}
|
||||
// @Failure 400 {object} APIResponse
|
||||
// @Failure 500 {object} APIResponse
|
||||
// @Router /contacts [post]
|
||||
func (h *ContactHandler) create(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var contact models.Contact
|
||||
if err := json.NewDecoder(r.Body).Decode(&contact); err != nil {
|
||||
BadRequest(w, ErrInvalidJSON, "Error parsing the contact")
|
||||
return
|
||||
}
|
||||
|
||||
if contact.Name == "" {
|
||||
err := ErrMissingRequired
|
||||
err.Field = "name"
|
||||
BadRequest(w, err, RequiredFieldErr("name"))
|
||||
return
|
||||
}
|
||||
|
||||
if contact.Company == "" {
|
||||
err := ErrMissingRequired
|
||||
err.Field = "company"
|
||||
BadRequest(w, err, RequiredFieldErr("company"))
|
||||
return
|
||||
}
|
||||
|
||||
if contact.Phone == "" {
|
||||
err := ErrMissingRequired
|
||||
err.Field = "phone"
|
||||
BadRequest(w, err, RequiredFieldErr("phone"))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := h.repository.Create(&contact)
|
||||
if err != nil {
|
||||
InternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
contact.ID = int(id)
|
||||
JSONSuccess(w, contact, "Contact created successfully", http.StatusCreated)
|
||||
}
|
||||
|
||||
// Update godoc
|
||||
// @Summary Update a contact
|
||||
// @Description Update an existing contact by ID
|
||||
// @Tags contacts
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Contact ID"
|
||||
// @Param contact body models.Contact true "Contact object"
|
||||
// @Success 200 {object} APIResponse
|
||||
// @Failure 400 {object} APIResponse
|
||||
// @Failure 404 {object} APIResponse
|
||||
// @Failure 500 {object} APIResponse
|
||||
// @Router /contacts/{id} [put]
|
||||
func (h *ContactHandler) update(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.PathValue("id"))
|
||||
if err != nil {
|
||||
BadRequest(w, ErrInvalidFormat, invalid_id)
|
||||
return
|
||||
}
|
||||
|
||||
var contact models.Contact
|
||||
if err := json.NewDecoder(r.Body).Decode(&contact); err != nil {
|
||||
BadRequest(w, ErrInvalidJSON, "Error parsing the contact")
|
||||
return
|
||||
}
|
||||
|
||||
contact.ID = id
|
||||
|
||||
updateErr := h.repository.Update(&contact)
|
||||
|
||||
if updateErr != nil {
|
||||
InternalError(w, updateErr)
|
||||
return
|
||||
}
|
||||
|
||||
JSONSuccess(w, nil, "Contact updated successfully", http.StatusOK)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
// @Summary Delete a contact
|
||||
// @Description Delete a contact by ID
|
||||
// @Tags contacts
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Contact ID"
|
||||
// @Success 200 {object} APIResponse
|
||||
// @Failure 400 {object} APIResponse
|
||||
// @Failure 404 {object} APIResponse
|
||||
// @Failure 500 {object} APIResponse
|
||||
// @Router /contacts/{id} [delete]
|
||||
func (h *ContactHandler) delete(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.PathValue("id"))
|
||||
|
||||
if err != nil {
|
||||
BadRequest(w, ErrInvalidFormat, invalid_id)
|
||||
return
|
||||
}
|
||||
rowsAffected, err := h.repository.Delete(id)
|
||||
|
||||
if err != nil {
|
||||
InternalError(w, err)
|
||||
return
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
BadRequest(w, ErrNotFound, "Contact not found")
|
||||
return
|
||||
}
|
||||
JSONSuccess(w, nil, "Contact deleted successfully", http.StatusOK)
|
||||
}
|
||||
88
internal/handler/errors.go
Normal file
88
internal/handler/errors.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
/*
|
||||
Domain (First Digit)
|
||||
|
||||
1xxx - Request/Input Errors
|
||||
|
||||
2xxx - Authentication/Authorization
|
||||
|
||||
3xxx - Business Logic/Validation
|
||||
|
||||
4xxx - Data/Resource Errors
|
||||
|
||||
5xxx - System/External Service Errors
|
||||
|
||||
Category (Second Digit)
|
||||
|
||||
x0xx - General/Generic errors in that domain
|
||||
|
||||
x1xx - Format/Structure errors
|
||||
|
||||
x2xx - Validation/Constraint errors
|
||||
|
||||
x3xx - State/Workflow errors
|
||||
|
||||
x4xx - Permission/Access errors
|
||||
|
||||
Specific Error (Last Two Digits)
|
||||
|
||||
xx00-xx99 - Specific error instances
|
||||
*/
|
||||
|
||||
// Domains
|
||||
const (
|
||||
DomainRequest = 1000
|
||||
DomainAuth = 2000
|
||||
DomainData = 4000
|
||||
DomainSystem = 5000
|
||||
)
|
||||
|
||||
const (
|
||||
CategoryGeneral = 0
|
||||
CategoryFormat = 100
|
||||
CategoryValidation = 200
|
||||
CategoryState = 300
|
||||
CategoryPermission = 400
|
||||
)
|
||||
|
||||
// Err code Domain + Category + Specific
|
||||
const (
|
||||
CodeInvalidJSON = DomainRequest + CategoryFormat + 1
|
||||
InvalidParamFormat = DomainRequest + CategoryFormat + 0
|
||||
CodeValidationFailed = DomainRequest + CategoryValidation + 0
|
||||
CodeMissingRequired = DomainRequest + CategoryValidation + 1
|
||||
CodeNotFound = DomainData + CategoryPermission + 0
|
||||
CodeInternalError = DomainSystem + CategoryGeneral + 0
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidJSON = APIError{Code: CodeInvalidJSON, Message: "Invalid JSON in request body"}
|
||||
ErrValidation = APIError{Code: CodeValidationFailed, Message: "Validation failed"}
|
||||
ErrMissingRequired = APIError{Code: CodeMissingRequired, Message: "Required field is missing"}
|
||||
ErrNotFound = APIError{Code: CodeNotFound, Message: "Resource not found"}
|
||||
ErrInternalServer = APIError{Code: CodeInternalError, Message: "Internal server error"}
|
||||
ErrInvalidFormat = APIError{Code: InvalidParamFormat, Message: "The given param doesn't match the format expectations"}
|
||||
)
|
||||
|
||||
// Helper functions for common errors
|
||||
func BadRequest(w http.ResponseWriter, err APIError, details string) {
|
||||
JSONError(w, err.Message, details, err.Code, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func NotFound(w http.ResponseWriter, err APIError, details string) {
|
||||
JSONError(w, err.Message, details, err.Code, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func InternalError(w http.ResponseWriter, err error) {
|
||||
JSONError(w, ErrInternalServer.Message, err.Error(), ErrInternalServer.Code, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func RequiredFieldErr(field string) string {
|
||||
return (fmt.Sprintf("%s %s", field, required_field))
|
||||
}
|
||||
16
internal/handler/health_handler.go
Normal file
16
internal/handler/health_handler.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HandlHealthChecks(mux *http.ServeMux) {
|
||||
routes := []Route{{"GET /health", healthCheck}}
|
||||
NewBaseHandler(mux, routes)
|
||||
}
|
||||
|
||||
func healthCheck(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
}
|
||||
65
internal/handler/response.go
Normal file
65
internal/handler/response.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type APIResponse struct {
|
||||
// Indicates if the request was successful
|
||||
Success bool `json:"success"`
|
||||
// The response data
|
||||
Data any `json:"data,omitempty"`
|
||||
// List of errors if any occurred
|
||||
Errors []APIError `json:"errors,omitempty"`
|
||||
// Optional message
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type APIError struct {
|
||||
// Error code
|
||||
Code int `json:"code"`
|
||||
// Human-readable error message
|
||||
Message string `json:"message"`
|
||||
// Additional error details
|
||||
Details string `json:"details,omitempty"`
|
||||
// Field name if applicable
|
||||
Field string `json:"field,omitempty" example:"name"`
|
||||
}
|
||||
|
||||
const content_type = "Content-Type"
|
||||
const application_json = "application/json"
|
||||
|
||||
func JSONSuccess(w http.ResponseWriter, data any, message string, statusCode int) {
|
||||
w.Header().Set(content_type, application_json)
|
||||
w.WriteHeader(statusCode)
|
||||
json.NewEncoder(w).Encode(APIResponse{
|
||||
Success: true,
|
||||
Data: data,
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
func JSONError(w http.ResponseWriter, message, details string, code, statusCode int) {
|
||||
w.Header().Set(content_type, application_json)
|
||||
w.WriteHeader(statusCode)
|
||||
json.NewEncoder(w).Encode(APIResponse{
|
||||
Success: false,
|
||||
Errors: []APIError{
|
||||
{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Details: details,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func JSONErrors(w http.ResponseWriter, errors []APIError, statusCode int) {
|
||||
w.Header().Set(content_type, application_json)
|
||||
w.WriteHeader(statusCode)
|
||||
json.NewEncoder(w).Encode(APIResponse{
|
||||
Success: false,
|
||||
Errors: errors,
|
||||
})
|
||||
}
|
||||
13
internal/models/contact.go
Normal file
13
internal/models/contact.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
// Contact represents a contact entity
|
||||
type Contact struct {
|
||||
// ID is the unique identifier for the contact
|
||||
ID int `json:"id" db:"id"`
|
||||
// Name of the contact
|
||||
Name string `json:"name" db:"name"`
|
||||
// Company the contact works for
|
||||
Company string `json:"company" db:"company"`
|
||||
// Phone number in international format
|
||||
Phone string `json:"phone" db:"phone"`
|
||||
}
|
||||
74
internal/repository/base_repository.go
Normal file
74
internal/repository/base_repository.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type baseRepository[T any] struct {
|
||||
db *sql.DB
|
||||
tableName string
|
||||
}
|
||||
|
||||
func NewBaseRepository[T any](db *sql.DB, tableName string) *baseRepository[T] {
|
||||
return &baseRepository[T]{
|
||||
db: db,
|
||||
tableName: tableName,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *baseRepository[T]) BuildQuery(baseQuery string) string {
|
||||
return fmt.Sprintf(baseQuery, r.tableName)
|
||||
}
|
||||
|
||||
func (r *baseRepository[T]) GetDB() *sql.DB {
|
||||
return r.db
|
||||
}
|
||||
|
||||
func (r *baseRepository[T]) GetAll() ([]T, error) {
|
||||
query := r.BuildQuery("SELECT * FROM %s ORDER BY id DESC")
|
||||
|
||||
rows, err := r.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
entities := make([]T, 0)
|
||||
rowsErr := ScanRows(rows, &entities)
|
||||
|
||||
if rowsErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
func (r *baseRepository[T]) GetByID(id int) (*T, error) {
|
||||
var entity T
|
||||
|
||||
query := r.BuildQuery("SELECT * FROM %s WHERE id = ?")
|
||||
row := r.db.QueryRow(
|
||||
query,
|
||||
id,
|
||||
)
|
||||
err := scanRow(row, &entity)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entity, nil
|
||||
}
|
||||
|
||||
func (r *baseRepository[T]) Delete(id int) (int64, error) {
|
||||
query := r.BuildQuery("DELETE FROM %s WHERE id = ?")
|
||||
res, err := r.db.Exec(query, id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
83
internal/repository/contact_repository.go
Normal file
83
internal/repository/contact_repository.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"gitea.gabilandia.com/gabdlr/agenda-web-go/internal/models"
|
||||
)
|
||||
|
||||
type ContactRepository struct {
|
||||
baseRepository[models.Contact]
|
||||
}
|
||||
|
||||
func NewContactRepository(db *sql.DB) Repository[models.Contact] {
|
||||
return &ContactRepository{
|
||||
baseRepository[models.Contact]{
|
||||
db: db,
|
||||
tableName: "contacts",
|
||||
}}
|
||||
}
|
||||
|
||||
func (r *ContactRepository) Create(contact *models.Contact) (int64, error) {
|
||||
query := r.BuildQuery("INSERT INTO %s (name, company, phone) VALUES (?, ?, ?)")
|
||||
|
||||
result, err := r.db.Exec(
|
||||
query,
|
||||
contact.Name, contact.Company, contact.Phone,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
contact.ID = int(id)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (r *ContactRepository) Update(contact *models.Contact) error {
|
||||
query := r.BuildQuery("UPDATE %s SET")
|
||||
fieldsToUpdate := make([]string, 0, 4)
|
||||
fields := make([]any, 0)
|
||||
|
||||
if contact.Name != "" {
|
||||
fieldsToUpdate = append(fieldsToUpdate, "name")
|
||||
fields = append(fields, &contact.Name)
|
||||
}
|
||||
|
||||
if contact.Company != "" {
|
||||
fieldsToUpdate = append(fieldsToUpdate, "company")
|
||||
fields = append(fields, &contact.Company)
|
||||
}
|
||||
|
||||
if contact.Phone != "" {
|
||||
fieldsToUpdate = append(fieldsToUpdate, "phone")
|
||||
fields = append(fields, &contact.Phone)
|
||||
}
|
||||
|
||||
fields = append(fields, &contact.ID)
|
||||
|
||||
fieldsToUpdatelen := len(fieldsToUpdate)
|
||||
for i, field := range fieldsToUpdate {
|
||||
query += fmt.Sprintf(" %s = ?", field)
|
||||
if i != fieldsToUpdatelen-1 {
|
||||
query += ","
|
||||
}
|
||||
}
|
||||
|
||||
query += " WHERE id = ?"
|
||||
|
||||
_, err := r.db.Exec(query,
|
||||
fields...,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
9
internal/repository/interfaces.go
Normal file
9
internal/repository/interfaces.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package repository
|
||||
|
||||
type Repository[T any] interface {
|
||||
Create(T *T) (int64, error)
|
||||
Delete(id int) (int64, error)
|
||||
GetAll() ([]T, error)
|
||||
GetByID(id int) (*T, error)
|
||||
Update(contact *T) error
|
||||
}
|
||||
55
internal/repository/scanner_helper.go
Normal file
55
internal/repository/scanner_helper.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func scanRow(row *sql.Row, dest any) error {
|
||||
destValue := reflect.ValueOf(dest).Elem()
|
||||
fields := make([]any, destValue.NumField())
|
||||
|
||||
for i := 0; i < destValue.NumField(); i++ {
|
||||
fields[i] = destValue.Field(i).Addr().Interface()
|
||||
}
|
||||
|
||||
return row.Scan(fields...)
|
||||
}
|
||||
|
||||
func ScanRows(rows *sql.Rows, destSlice any) error {
|
||||
sliceValue := reflect.ValueOf(destSlice)
|
||||
if sliceValue.Kind() != reflect.Pointer || sliceValue.Elem().Kind() != reflect.Slice {
|
||||
return fmt.Errorf("destSlice must be a pointer to a slice")
|
||||
}
|
||||
|
||||
sliceElem := sliceValue.Elem()
|
||||
structType := sliceElem.Type().Elem()
|
||||
|
||||
for rows.Next() {
|
||||
newStruct := reflect.New(structType).Elem()
|
||||
|
||||
fields := make([]any, newStruct.NumField())
|
||||
for i := 0; i < newStruct.NumField(); i++ {
|
||||
fields[i] = newStruct.Field(i).Addr().Interface()
|
||||
}
|
||||
|
||||
if err := rows.Scan(fields...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sliceElem.Set(reflect.Append(sliceElem, newStruct))
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func GetStructFieldsPtr(object any) []any {
|
||||
destValue := reflect.ValueOf(object).Elem()
|
||||
fields := make([]any, destValue.NumField())
|
||||
|
||||
for i := 0; i < destValue.NumField(); i++ {
|
||||
fields[i] = destValue.Field(i).Addr().Interface()
|
||||
}
|
||||
return fields
|
||||
}
|
||||
Reference in New Issue
Block a user