{"category": "fiber", "text": "\n go1.21\n controllers\n\n// controllers/patientController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// PatientController handles HTTP requests for the Patient resource.\ntype PatientController struct {\n\tPatientService contracts.PatientServiceContract\n}\n\n// NewPatientController constructs a PatientController with its service dependency.\nfunc NewPatientController(svc contracts.PatientServiceContract) *PatientController {\n\treturn &PatientController{PatientService: svc}\n}\n\n// CreatePatient godoc\n// @Summary Create a new patient\n// @Description Create a new patient in the system\n// @Tags Patients\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.PatientInputDTO true \"Patient data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /patients [post]\nfunc (ctrl *PatientController) CreatePatient(c *fiber.Ctx) error {\n\tvar input contracts.PatientInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.Name == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"name is required\"})\n\t}\n\tif input.Contact == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"contact is required\"})\n\t}\n\n\tid, err := ctrl.PatientService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Patient created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get patient by CPF\n// @Description Retrieve a single patient identified by its CPF\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Patient CPF\"\n// @Success 200 {object} contracts.PatientDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /patients/{cpf} [get]\nfunc (ctrl *PatientController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.PatientService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListPatients godoc\n// @Summary List all patients\n// @Description Returns a paginated list of patients\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.PatientDTO\n// @Failure 500 {object} fiber.Map\n// @Router /patients [get]\nfunc (ctrl *PatientController) ListPatients(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.PatientService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve patients\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"medical-sas-api/controllers\"\n\t\"medical-sas-api/initializers\"\n\t_ \"medical-sas-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewPatientController(svc.PatientService)\n\tpatients := protected.Group(\"/patients\")\n\tpatients.Post(\"/\", entityCtrl.CreatePatient)\n\tpatients.Get(\"/\", entityCtrl.ListPatients)\n\tpatients.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"medical-sas-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n controllers\n\n// controllers/patientController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// PatientController handles HTTP requests for the Patient resource.\ntype PatientController struct {\n\tPatientService contracts.PatientServiceContract\n}\n\n// NewPatientController constructs a PatientController with its service dependency.\nfunc NewPatientController(svc contracts.PatientServiceContract) *PatientController {\n\treturn &PatientController{PatientService: svc}\n}\n\n// CreatePatient godoc\n// @Summary Create a new patient\n// @Description Create a new patient in the system\n// @Tags Patients\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.PatientInputDTO true \"Patient data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /patients [post]\nfunc (ctrl *PatientController) CreatePatient(c *fiber.Ctx) error {\n\tvar input contracts.PatientInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.Name == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"name is required\"})\n\t}\n\tif input.Contact == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"contact is required\"})\n\t}\n\n\tid, err := ctrl.PatientService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Patient created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get patient by CPF\n// @Description Retrieve a single patient identified by its CPF\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Patient CPF\"\n// @Success 200 {object} contracts.PatientDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /patients/{cpf} [get]\nfunc (ctrl *PatientController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.PatientService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListPatients godoc\n// @Summary List all patients\n// @Description Returns a paginated list of patients\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.PatientDTO\n// @Failure 500 {object} fiber.Map\n// @Router /patients [get]\nfunc (ctrl *PatientController) ListPatients(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.PatientService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve patients\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"medical-sas-api/controllers\"\n\t\"medical-sas-api/initializers\"\n\t_ \"medical-sas-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewPatientController(svc.PatientService)\n\tpatients := protected.Group(\"/patients\")\n\tpatients.Post(\"/\", entityCtrl.CreatePatient)\n\tpatients.Get(\"/\", entityCtrl.ListPatients)\n\tpatients.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"medical-sas-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n controllers\n\n// controllers/patientController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// PatientController handles HTTP requests for the Patient resource.\ntype PatientController struct {\n\tPatientService contracts.PatientServiceContract\n}\n\n// NewPatientController constructs a PatientController with its service dependency.\nfunc NewPatientController(svc contracts.PatientServiceContract) *PatientController {\n\treturn &PatientController{PatientService: svc}\n}\n\n// CreatePatient godoc\n// @Summary Create a new patient\n// @Description Create a new patient in the system\n// @Tags Patients\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.PatientInputDTO true \"Patient data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /patients [post]\nfunc (ctrl *PatientController) CreatePatient(c *fiber.Ctx) error {\n\tvar input contracts.PatientInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.Name == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"name is required\"})\n\t}\n\tif input.Contact == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"contact is required\"})\n\t}\n\n\tid, err := ctrl.PatientService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Patient created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get patient by CPF\n// @Description Retrieve a single patient identified by its CPF\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Patient CPF\"\n// @Success 200 {object} contracts.PatientDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /patients/{cpf} [get]\nfunc (ctrl *PatientController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.PatientService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListPatients godoc\n// @Summary List all patients\n// @Description Returns a paginated list of patients\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.PatientDTO\n// @Failure 500 {object} fiber.Map\n// @Router /patients [get]\nfunc (ctrl *PatientController) ListPatients(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.PatientService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve patients\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"medical-sas-api/controllers\"\n\t\"medical-sas-api/initializers\"\n\t_ \"medical-sas-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewPatientController(svc.PatientService)\n\tpatients := protected.Group(\"/patients\")\n\tpatients.Post(\"/\", entityCtrl.CreatePatient)\n\tpatients.Get(\"/\", entityCtrl.ListPatients)\n\tpatients.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"medical-sas-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n controllers\n\n// controllers/patientController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// PatientController handles HTTP requests for the Patient resource.\ntype PatientController struct {\n\tPatientService contracts.PatientServiceContract\n}\n\n// NewPatientController constructs a PatientController with its service dependency.\nfunc NewPatientController(svc contracts.PatientServiceContract) *PatientController {\n\treturn &PatientController{PatientService: svc}\n}\n\n// CreatePatient godoc\n// @Summary Create a new patient\n// @Description Create a new patient in the system\n// @Tags Patients\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.PatientInputDTO true \"Patient data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /patients [post]\nfunc (ctrl *PatientController) CreatePatient(c *fiber.Ctx) error {\n\tvar input contracts.PatientInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.Name == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"name is required\"})\n\t}\n\tif input.Contact == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"contact is required\"})\n\t}\n\n\tid, err := ctrl.PatientService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Patient created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get patient by CPF\n// @Description Retrieve a single patient identified by its CPF\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Patient CPF\"\n// @Success 200 {object} contracts.PatientDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /patients/{cpf} [get]\nfunc (ctrl *PatientController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.PatientService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListPatients godoc\n// @Summary List all patients\n// @Description Returns a paginated list of patients\n// @Tags Patients\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.PatientDTO\n// @Failure 500 {object} fiber.Map\n// @Router /patients [get]\nfunc (ctrl *PatientController) ListPatients(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.PatientService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve patients\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"medical-sas-api/controllers\"\n\t\"medical-sas-api/initializers\"\n\t_ \"medical-sas-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewPatientController(svc.PatientService)\n\tpatients := protected.Group(\"/patients\")\n\tpatients.Post(\"/\", entityCtrl.CreatePatient)\n\tpatients.Get(\"/\", entityCtrl.ListPatients)\n\tpatients.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"medical-sas-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n utils\n\n// utils/responseUtil.go\n// Pattern: response utility interface + JSON helper\n// Observed in: Medical-App-Core/utils/responseUtil.go\npackage utils\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ResponseUtil abstracts JSON response construction for testability.\ntype ResponseUtil interface {\n\tRespondWithJSON(c *fiber.Ctx, code int, payload any) error\n\tRespondWithError(c *fiber.Ctx, code int, message string) error\n}\n\n// JSONResponseUtil implements ResponseUtil using Fiber's context.\ntype JSONResponseUtil struct{}\n\nfunc (ru *JSONResponseUtil) RespondWithJSON(c *fiber.Ctx, code int, payload any) error {\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn c.Status(fiber.StatusInternalServerError).\n\t\t\tJSON(fiber.Map{\"error\": \"failed to marshal response\"})\n\t}\n\tc.Set(\"Content-Type\", \"application/json\")\n\treturn c.Status(code).Send(body)\n}\n\nfunc (ru *JSONResponseUtil) RespondWithError(c *fiber.Ctx, code int, message string) error {\n\treturn c.Status(code).JSON(fiber.Map{\"error\": message})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n controllers\n\n// controllers/orderController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"ecommerce-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// OrderController handles HTTP requests for the Order resource.\ntype OrderController struct {\n\tOrderService contracts.OrderServiceContract\n}\n\n// NewOrderController constructs a OrderController with its service dependency.\nfunc NewOrderController(svc contracts.OrderServiceContract) *OrderController {\n\treturn &OrderController{OrderService: svc}\n}\n\n// CreateOrder godoc\n// @Summary Create a new order\n// @Description Create a new order in the system\n// @Tags Orders\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.OrderInputDTO true \"Order data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /orders [post]\nfunc (ctrl *OrderController) CreateOrder(c *fiber.Ctx) error {\n\tvar input contracts.OrderInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.CustomerID == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"customer_id is required\"})\n\t}\n\tif len(input.Items) == 0 {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"items cannot be empty\"})\n\t}\n\n\tid, err := ctrl.OrderService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Order created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByOrderNumber godoc\n// @Summary Get order by OrderNumber\n// @Description Retrieve a single order identified by its OrderNumber\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param order_number path string true \"Order OrderNumber\"\n// @Success 200 {object} contracts.OrderDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /orders/{order_number} [get]\nfunc (ctrl *OrderController) GetByOrderNumber(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"order_number\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"order_number is required\"})\n\t}\n\n\tresult, err := ctrl.OrderService.FindByOrderNumber(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListOrders godoc\n// @Summary List all orders\n// @Description Returns a paginated list of orders\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.OrderDTO\n// @Failure 500 {object} fiber.Map\n// @Router /orders [get]\nfunc (ctrl *OrderController) ListOrders(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.OrderService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve orders\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"ecommerce-api/controllers\"\n\t\"ecommerce-api/initializers\"\n\t_ \"ecommerce-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewOrderController(svc.OrderService)\n\torders := protected.Group(\"/orders\")\n\torders.Post(\"/\", entityCtrl.CreateOrder)\n\torders.Get(\"/\", entityCtrl.ListOrders)\n\torders.Get(\"/:order_number\", entityCtrl.GetByOrderNumber)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"ecommerce-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n controllers\n\n// controllers/orderController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"ecommerce-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// OrderController handles HTTP requests for the Order resource.\ntype OrderController struct {\n\tOrderService contracts.OrderServiceContract\n}\n\n// NewOrderController constructs a OrderController with its service dependency.\nfunc NewOrderController(svc contracts.OrderServiceContract) *OrderController {\n\treturn &OrderController{OrderService: svc}\n}\n\n// CreateOrder godoc\n// @Summary Create a new order\n// @Description Create a new order in the system\n// @Tags Orders\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.OrderInputDTO true \"Order data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /orders [post]\nfunc (ctrl *OrderController) CreateOrder(c *fiber.Ctx) error {\n\tvar input contracts.OrderInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.CustomerID == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"customer_id is required\"})\n\t}\n\tif len(input.Items) == 0 {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"items cannot be empty\"})\n\t}\n\n\tid, err := ctrl.OrderService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Order created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByOrderNumber godoc\n// @Summary Get order by OrderNumber\n// @Description Retrieve a single order identified by its OrderNumber\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param order_number path string true \"Order OrderNumber\"\n// @Success 200 {object} contracts.OrderDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /orders/{order_number} [get]\nfunc (ctrl *OrderController) GetByOrderNumber(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"order_number\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"order_number is required\"})\n\t}\n\n\tresult, err := ctrl.OrderService.FindByOrderNumber(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListOrders godoc\n// @Summary List all orders\n// @Description Returns a paginated list of orders\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.OrderDTO\n// @Failure 500 {object} fiber.Map\n// @Router /orders [get]\nfunc (ctrl *OrderController) ListOrders(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.OrderService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve orders\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"ecommerce-api/controllers\"\n\t\"ecommerce-api/initializers\"\n\t_ \"ecommerce-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewOrderController(svc.OrderService)\n\torders := protected.Group(\"/orders\")\n\torders.Post(\"/\", entityCtrl.CreateOrder)\n\torders.Get(\"/\", entityCtrl.ListOrders)\n\torders.Get(\"/:order_number\", entityCtrl.GetByOrderNumber)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"ecommerce-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n controllers\n\n// controllers/orderController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"ecommerce-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// OrderController handles HTTP requests for the Order resource.\ntype OrderController struct {\n\tOrderService contracts.OrderServiceContract\n}\n\n// NewOrderController constructs a OrderController with its service dependency.\nfunc NewOrderController(svc contracts.OrderServiceContract) *OrderController {\n\treturn &OrderController{OrderService: svc}\n}\n\n// CreateOrder godoc\n// @Summary Create a new order\n// @Description Create a new order in the system\n// @Tags Orders\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.OrderInputDTO true \"Order data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /orders [post]\nfunc (ctrl *OrderController) CreateOrder(c *fiber.Ctx) error {\n\tvar input contracts.OrderInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.CustomerID == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"customer_id is required\"})\n\t}\n\tif len(input.Items) == 0 {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"items cannot be empty\"})\n\t}\n\n\tid, err := ctrl.OrderService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Order created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByOrderNumber godoc\n// @Summary Get order by OrderNumber\n// @Description Retrieve a single order identified by its OrderNumber\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param order_number path string true \"Order OrderNumber\"\n// @Success 200 {object} contracts.OrderDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /orders/{order_number} [get]\nfunc (ctrl *OrderController) GetByOrderNumber(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"order_number\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"order_number is required\"})\n\t}\n\n\tresult, err := ctrl.OrderService.FindByOrderNumber(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListOrders godoc\n// @Summary List all orders\n// @Description Returns a paginated list of orders\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.OrderDTO\n// @Failure 500 {object} fiber.Map\n// @Router /orders [get]\nfunc (ctrl *OrderController) ListOrders(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.OrderService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve orders\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"ecommerce-api/controllers\"\n\t\"ecommerce-api/initializers\"\n\t_ \"ecommerce-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewOrderController(svc.OrderService)\n\torders := protected.Group(\"/orders\")\n\torders.Post(\"/\", entityCtrl.CreateOrder)\n\torders.Get(\"/\", entityCtrl.ListOrders)\n\torders.Get(\"/:order_number\", entityCtrl.GetByOrderNumber)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"ecommerce-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n controllers\n\n// controllers/orderController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"ecommerce-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// OrderController handles HTTP requests for the Order resource.\ntype OrderController struct {\n\tOrderService contracts.OrderServiceContract\n}\n\n// NewOrderController constructs a OrderController with its service dependency.\nfunc NewOrderController(svc contracts.OrderServiceContract) *OrderController {\n\treturn &OrderController{OrderService: svc}\n}\n\n// CreateOrder godoc\n// @Summary Create a new order\n// @Description Create a new order in the system\n// @Tags Orders\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.OrderInputDTO true \"Order data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /orders [post]\nfunc (ctrl *OrderController) CreateOrder(c *fiber.Ctx) error {\n\tvar input contracts.OrderInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.CustomerID == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"customer_id is required\"})\n\t}\n\tif len(input.Items) == 0 {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"items cannot be empty\"})\n\t}\n\n\tid, err := ctrl.OrderService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Order created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByOrderNumber godoc\n// @Summary Get order by OrderNumber\n// @Description Retrieve a single order identified by its OrderNumber\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param order_number path string true \"Order OrderNumber\"\n// @Success 200 {object} contracts.OrderDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /orders/{order_number} [get]\nfunc (ctrl *OrderController) GetByOrderNumber(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"order_number\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"order_number is required\"})\n\t}\n\n\tresult, err := ctrl.OrderService.FindByOrderNumber(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListOrders godoc\n// @Summary List all orders\n// @Description Returns a paginated list of orders\n// @Tags Orders\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.OrderDTO\n// @Failure 500 {object} fiber.Map\n// @Router /orders [get]\nfunc (ctrl *OrderController) ListOrders(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.OrderService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve orders\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"ecommerce-api/controllers\"\n\t\"ecommerce-api/initializers\"\n\t_ \"ecommerce-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewOrderController(svc.OrderService)\n\torders := protected.Group(\"/orders\")\n\torders.Post(\"/\", entityCtrl.CreateOrder)\n\torders.Get(\"/\", entityCtrl.ListOrders)\n\torders.Get(\"/:order_number\", entityCtrl.GetByOrderNumber)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"ecommerce-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n utils\n\n// utils/responseUtil.go\n// Pattern: response utility interface + JSON helper\n// Observed in: Medical-App-Core/utils/responseUtil.go\npackage utils\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ResponseUtil abstracts JSON response construction for testability.\ntype ResponseUtil interface {\n\tRespondWithJSON(c *fiber.Ctx, code int, payload any) error\n\tRespondWithError(c *fiber.Ctx, code int, message string) error\n}\n\n// JSONResponseUtil implements ResponseUtil using Fiber's context.\ntype JSONResponseUtil struct{}\n\nfunc (ru *JSONResponseUtil) RespondWithJSON(c *fiber.Ctx, code int, payload any) error {\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn c.Status(fiber.StatusInternalServerError).\n\t\t\tJSON(fiber.Map{\"error\": \"failed to marshal response\"})\n\t}\n\tc.Set(\"Content-Type\", \"application/json\")\n\treturn c.Status(code).Send(body)\n}\n\nfunc (ru *JSONResponseUtil) RespondWithError(c *fiber.Ctx, code int, message string) error {\n\treturn c.Status(code).JSON(fiber.Map{\"error\": message})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n controllers\n\n// controllers/employeeController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"hrm-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// EmployeeController handles HTTP requests for the Employee resource.\ntype EmployeeController struct {\n\tEmployeeService contracts.EmployeeServiceContract\n}\n\n// NewEmployeeController constructs a EmployeeController with its service dependency.\nfunc NewEmployeeController(svc contracts.EmployeeServiceContract) *EmployeeController {\n\treturn &EmployeeController{EmployeeService: svc}\n}\n\n// CreateEmployee godoc\n// @Summary Create a new employee\n// @Description Create a new employee in the system\n// @Tags Employees\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.EmployeeInputDTO true \"Employee data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /employees [post]\nfunc (ctrl *EmployeeController) CreateEmployee(c *fiber.Ctx) error {\n\tvar input contracts.EmployeeInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.DepartmentName == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"department_name is required\"})\n\t}\n\tif input.Position == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"position is required\"})\n\t}\n\n\tid, err := ctrl.EmployeeService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Employee created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get employee by CPF\n// @Description Retrieve a single employee identified by its CPF\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Employee CPF\"\n// @Success 200 {object} contracts.EmployeeDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /employees/{cpf} [get]\nfunc (ctrl *EmployeeController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.EmployeeService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListEmployees godoc\n// @Summary List all employees\n// @Description Returns a paginated list of employees\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.EmployeeDTO\n// @Failure 500 {object} fiber.Map\n// @Router /employees [get]\nfunc (ctrl *EmployeeController) ListEmployees(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.EmployeeService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve employees\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"hrm-api/controllers\"\n\t\"hrm-api/initializers\"\n\t_ \"hrm-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewEmployeeController(svc.EmployeeService)\n\temployees := protected.Group(\"/employees\")\n\temployees.Post(\"/\", entityCtrl.CreateEmployee)\n\temployees.Get(\"/\", entityCtrl.ListEmployees)\n\temployees.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"hrm-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n controllers\n\n// controllers/employeeController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"hrm-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// EmployeeController handles HTTP requests for the Employee resource.\ntype EmployeeController struct {\n\tEmployeeService contracts.EmployeeServiceContract\n}\n\n// NewEmployeeController constructs a EmployeeController with its service dependency.\nfunc NewEmployeeController(svc contracts.EmployeeServiceContract) *EmployeeController {\n\treturn &EmployeeController{EmployeeService: svc}\n}\n\n// CreateEmployee godoc\n// @Summary Create a new employee\n// @Description Create a new employee in the system\n// @Tags Employees\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.EmployeeInputDTO true \"Employee data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /employees [post]\nfunc (ctrl *EmployeeController) CreateEmployee(c *fiber.Ctx) error {\n\tvar input contracts.EmployeeInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.DepartmentName == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"department_name is required\"})\n\t}\n\tif input.Position == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"position is required\"})\n\t}\n\n\tid, err := ctrl.EmployeeService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Employee created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get employee by CPF\n// @Description Retrieve a single employee identified by its CPF\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Employee CPF\"\n// @Success 200 {object} contracts.EmployeeDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /employees/{cpf} [get]\nfunc (ctrl *EmployeeController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.EmployeeService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListEmployees godoc\n// @Summary List all employees\n// @Description Returns a paginated list of employees\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.EmployeeDTO\n// @Failure 500 {object} fiber.Map\n// @Router /employees [get]\nfunc (ctrl *EmployeeController) ListEmployees(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.EmployeeService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve employees\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"hrm-api/controllers\"\n\t\"hrm-api/initializers\"\n\t_ \"hrm-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewEmployeeController(svc.EmployeeService)\n\temployees := protected.Group(\"/employees\")\n\temployees.Post(\"/\", entityCtrl.CreateEmployee)\n\temployees.Get(\"/\", entityCtrl.ListEmployees)\n\temployees.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"hrm-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n controllers\n\n// controllers/employeeController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"hrm-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// EmployeeController handles HTTP requests for the Employee resource.\ntype EmployeeController struct {\n\tEmployeeService contracts.EmployeeServiceContract\n}\n\n// NewEmployeeController constructs a EmployeeController with its service dependency.\nfunc NewEmployeeController(svc contracts.EmployeeServiceContract) *EmployeeController {\n\treturn &EmployeeController{EmployeeService: svc}\n}\n\n// CreateEmployee godoc\n// @Summary Create a new employee\n// @Description Create a new employee in the system\n// @Tags Employees\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.EmployeeInputDTO true \"Employee data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /employees [post]\nfunc (ctrl *EmployeeController) CreateEmployee(c *fiber.Ctx) error {\n\tvar input contracts.EmployeeInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.DepartmentName == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"department_name is required\"})\n\t}\n\tif input.Position == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"position is required\"})\n\t}\n\n\tid, err := ctrl.EmployeeService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Employee created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get employee by CPF\n// @Description Retrieve a single employee identified by its CPF\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Employee CPF\"\n// @Success 200 {object} contracts.EmployeeDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /employees/{cpf} [get]\nfunc (ctrl *EmployeeController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.EmployeeService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListEmployees godoc\n// @Summary List all employees\n// @Description Returns a paginated list of employees\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.EmployeeDTO\n// @Failure 500 {object} fiber.Map\n// @Router /employees [get]\nfunc (ctrl *EmployeeController) ListEmployees(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.EmployeeService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve employees\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"hrm-api/controllers\"\n\t\"hrm-api/initializers\"\n\t_ \"hrm-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewEmployeeController(svc.EmployeeService)\n\temployees := protected.Group(\"/employees\")\n\temployees.Post(\"/\", entityCtrl.CreateEmployee)\n\temployees.Get(\"/\", entityCtrl.ListEmployees)\n\temployees.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"hrm-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n controllers\n\n// controllers/employeeController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"hrm-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// EmployeeController handles HTTP requests for the Employee resource.\ntype EmployeeController struct {\n\tEmployeeService contracts.EmployeeServiceContract\n}\n\n// NewEmployeeController constructs a EmployeeController with its service dependency.\nfunc NewEmployeeController(svc contracts.EmployeeServiceContract) *EmployeeController {\n\treturn &EmployeeController{EmployeeService: svc}\n}\n\n// CreateEmployee godoc\n// @Summary Create a new employee\n// @Description Create a new employee in the system\n// @Tags Employees\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.EmployeeInputDTO true \"Employee data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /employees [post]\nfunc (ctrl *EmployeeController) CreateEmployee(c *fiber.Ctx) error {\n\tvar input contracts.EmployeeInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.DepartmentName == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"department_name is required\"})\n\t}\n\tif input.Position == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"position is required\"})\n\t}\n\n\tid, err := ctrl.EmployeeService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Employee created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByCPF godoc\n// @Summary Get employee by CPF\n// @Description Retrieve a single employee identified by its CPF\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param cpf path string true \"Employee CPF\"\n// @Success 200 {object} contracts.EmployeeDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /employees/{cpf} [get]\nfunc (ctrl *EmployeeController) GetByCPF(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"cpf\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"cpf is required\"})\n\t}\n\n\tresult, err := ctrl.EmployeeService.FindByCPF(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListEmployees godoc\n// @Summary List all employees\n// @Description Returns a paginated list of employees\n// @Tags Employees\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.EmployeeDTO\n// @Failure 500 {object} fiber.Map\n// @Router /employees [get]\nfunc (ctrl *EmployeeController) ListEmployees(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.EmployeeService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve employees\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"hrm-api/controllers\"\n\t\"hrm-api/initializers\"\n\t_ \"hrm-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewEmployeeController(svc.EmployeeService)\n\temployees := protected.Group(\"/employees\")\n\temployees.Post(\"/\", entityCtrl.CreateEmployee)\n\temployees.Get(\"/\", entityCtrl.ListEmployees)\n\temployees.Get(\"/:cpf\", entityCtrl.GetByCPF)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"hrm-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n utils\n\n// utils/responseUtil.go\n// Pattern: response utility interface + JSON helper\n// Observed in: Medical-App-Core/utils/responseUtil.go\npackage utils\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ResponseUtil abstracts JSON response construction for testability.\ntype ResponseUtil interface {\n\tRespondWithJSON(c *fiber.Ctx, code int, payload any) error\n\tRespondWithError(c *fiber.Ctx, code int, message string) error\n}\n\n// JSONResponseUtil implements ResponseUtil using Fiber's context.\ntype JSONResponseUtil struct{}\n\nfunc (ru *JSONResponseUtil) RespondWithJSON(c *fiber.Ctx, code int, payload any) error {\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn c.Status(fiber.StatusInternalServerError).\n\t\t\tJSON(fiber.Map{\"error\": \"failed to marshal response\"})\n\t}\n\tc.Set(\"Content-Type\", \"application/json\")\n\treturn c.Status(code).Send(body)\n}\n\nfunc (ru *JSONResponseUtil) RespondWithError(c *fiber.Ctx, code int, message string) error {\n\treturn c.Status(code).JSON(fiber.Map{\"error\": message})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n controllers\n\n// controllers/shipmentController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"logistics-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ShipmentController handles HTTP requests for the Shipment resource.\ntype ShipmentController struct {\n\tShipmentService contracts.ShipmentServiceContract\n}\n\n// NewShipmentController constructs a ShipmentController with its service dependency.\nfunc NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {\n\treturn &ShipmentController{ShipmentService: svc}\n}\n\n// CreateShipment godoc\n// @Summary Create a new shipment\n// @Description Create a new shipment in the system\n// @Tags Shipments\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.ShipmentInputDTO true \"Shipment data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [post]\nfunc (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {\n\tvar input contracts.ShipmentInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.OriginAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"origin_address is required\"})\n\t}\n\tif input.DestinationAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"destination_address is required\"})\n\t}\n\n\tid, err := ctrl.ShipmentService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Shipment created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByTrackingCode godoc\n// @Summary Get shipment by TrackingCode\n// @Description Retrieve a single shipment identified by its TrackingCode\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param tracking_code path string true \"Shipment TrackingCode\"\n// @Success 200 {object} contracts.ShipmentDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /shipments/{tracking_code} [get]\nfunc (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"tracking_code\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"tracking_code is required\"})\n\t}\n\n\tresult, err := ctrl.ShipmentService.FindByTrackingCode(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListShipments godoc\n// @Summary List all shipments\n// @Description Returns a paginated list of shipments\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.ShipmentDTO\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [get]\nfunc (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.ShipmentService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve shipments\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.21\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"logistics-api/controllers\"\n\t\"logistics-api/initializers\"\n\t_ \"logistics-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewShipmentController(svc.ShipmentService)\n\tshipments := protected.Group(\"/shipments\")\n\tshipments.Post(\"/\", entityCtrl.CreateShipment)\n\tshipments.Get(\"/\", entityCtrl.ListShipments)\n\tshipments.Get(\"/:tracking_code\", entityCtrl.GetByTrackingCode)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"logistics-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n controllers\n\n// controllers/shipmentController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"logistics-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ShipmentController handles HTTP requests for the Shipment resource.\ntype ShipmentController struct {\n\tShipmentService contracts.ShipmentServiceContract\n}\n\n// NewShipmentController constructs a ShipmentController with its service dependency.\nfunc NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {\n\treturn &ShipmentController{ShipmentService: svc}\n}\n\n// CreateShipment godoc\n// @Summary Create a new shipment\n// @Description Create a new shipment in the system\n// @Tags Shipments\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.ShipmentInputDTO true \"Shipment data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [post]\nfunc (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {\n\tvar input contracts.ShipmentInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.OriginAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"origin_address is required\"})\n\t}\n\tif input.DestinationAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"destination_address is required\"})\n\t}\n\n\tid, err := ctrl.ShipmentService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Shipment created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByTrackingCode godoc\n// @Summary Get shipment by TrackingCode\n// @Description Retrieve a single shipment identified by its TrackingCode\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param tracking_code path string true \"Shipment TrackingCode\"\n// @Success 200 {object} contracts.ShipmentDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /shipments/{tracking_code} [get]\nfunc (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"tracking_code\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"tracking_code is required\"})\n\t}\n\n\tresult, err := ctrl.ShipmentService.FindByTrackingCode(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListShipments godoc\n// @Summary List all shipments\n// @Description Returns a paginated list of shipments\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.ShipmentDTO\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [get]\nfunc (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.ShipmentService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve shipments\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.22\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"logistics-api/controllers\"\n\t\"logistics-api/initializers\"\n\t_ \"logistics-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewShipmentController(svc.ShipmentService)\n\tshipments := protected.Group(\"/shipments\")\n\tshipments.Post(\"/\", entityCtrl.CreateShipment)\n\tshipments.Get(\"/\", entityCtrl.ListShipments)\n\tshipments.Get(\"/:tracking_code\", entityCtrl.GetByTrackingCode)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"logistics-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n controllers\n\n// controllers/shipmentController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"logistics-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ShipmentController handles HTTP requests for the Shipment resource.\ntype ShipmentController struct {\n\tShipmentService contracts.ShipmentServiceContract\n}\n\n// NewShipmentController constructs a ShipmentController with its service dependency.\nfunc NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {\n\treturn &ShipmentController{ShipmentService: svc}\n}\n\n// CreateShipment godoc\n// @Summary Create a new shipment\n// @Description Create a new shipment in the system\n// @Tags Shipments\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.ShipmentInputDTO true \"Shipment data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [post]\nfunc (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {\n\tvar input contracts.ShipmentInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.OriginAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"origin_address is required\"})\n\t}\n\tif input.DestinationAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"destination_address is required\"})\n\t}\n\n\tid, err := ctrl.ShipmentService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Shipment created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByTrackingCode godoc\n// @Summary Get shipment by TrackingCode\n// @Description Retrieve a single shipment identified by its TrackingCode\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param tracking_code path string true \"Shipment TrackingCode\"\n// @Success 200 {object} contracts.ShipmentDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /shipments/{tracking_code} [get]\nfunc (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"tracking_code\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"tracking_code is required\"})\n\t}\n\n\tresult, err := ctrl.ShipmentService.FindByTrackingCode(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListShipments godoc\n// @Summary List all shipments\n// @Description Returns a paginated list of shipments\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.ShipmentDTO\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [get]\nfunc (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.ShipmentService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve shipments\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.23\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"logistics-api/controllers\"\n\t\"logistics-api/initializers\"\n\t_ \"logistics-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewShipmentController(svc.ShipmentService)\n\tshipments := protected.Group(\"/shipments\")\n\tshipments.Post(\"/\", entityCtrl.CreateShipment)\n\tshipments.Get(\"/\", entityCtrl.ListShipments)\n\tshipments.Get(\"/:tracking_code\", entityCtrl.GetByTrackingCode)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"logistics-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n controllers\n\n// controllers/shipmentController.go\n// Pattern: struct controller + constructor injection + Swagger annotations\n// Observed in: Medical-App-Core/controllers/\npackage controllers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"logistics-api/services/contracts\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ShipmentController handles HTTP requests for the Shipment resource.\ntype ShipmentController struct {\n\tShipmentService contracts.ShipmentServiceContract\n}\n\n// NewShipmentController constructs a ShipmentController with its service dependency.\nfunc NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {\n\treturn &ShipmentController{ShipmentService: svc}\n}\n\n// CreateShipment godoc\n// @Summary Create a new shipment\n// @Description Create a new shipment in the system\n// @Tags Shipments\n// @Accept json\n// @Produce json\n// @Security BearerAuth\n// @Param body body contracts.ShipmentInputDTO true \"Shipment data\"\n// @Success 201 {object} fiber.Map\n// @Failure 400 {object} fiber.Map\n// @Failure 422 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [post]\nfunc (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {\n\tvar input contracts.ShipmentInputDTO\n\tif err := c.BodyParser(&input); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\n\tif input.OriginAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"origin_address is required\"})\n\t}\n\tif input.DestinationAddress == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"destination_address is required\"})\n\t}\n\n\tid, err := ctrl.ShipmentService.CreateFromInput(input)\n\tif err != nil {\n\t\treturn c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusCreated).JSON(fiber.Map{\n\t\t\"message\": \"Shipment created successfully\",\n\t\t\"id\": id,\n\t})\n}\n\n// GetByTrackingCode godoc\n// @Summary Get shipment by TrackingCode\n// @Description Retrieve a single shipment identified by its TrackingCode\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param tracking_code path string true \"Shipment TrackingCode\"\n// @Success 200 {object} contracts.ShipmentDTO\n// @Failure 400 {object} fiber.Map\n// @Failure 404 {object} fiber.Map\n// @Router /shipments/{tracking_code} [get]\nfunc (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {\n\tidentifier := c.Params(\"tracking_code\")\n\tif identifier == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"tracking_code is required\"})\n\t}\n\n\tresult, err := ctrl.ShipmentService.FindByTrackingCode(identifier)\n\tif err != nil {\n\t\treturn c.Status(http.StatusNotFound).JSON(fiber.Map{\"error\": err.Error()})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(result)\n}\n\n// ListShipments godoc\n// @Summary List all shipments\n// @Description Returns a paginated list of shipments\n// @Tags Shipments\n// @Produce json\n// @Security BearerAuth\n// @Param page query int false \"Page number (default 1)\"\n// @Param limit query int false \"Page size (default 20)\"\n// @Success 200 {array} contracts.ShipmentDTO\n// @Failure 500 {object} fiber.Map\n// @Router /shipments [get]\nfunc (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {\n\tpage := c.QueryInt(\"page\", 1)\n\tlimit := c.QueryInt(\"limit\", 20)\n\tif page < 1 { page = 1 }\n\tif limit < 1 { limit = 20 }\n\n\tresults, err := ctrl.ShipmentService.List(page, limit)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to retrieve shipments\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\n\t\t\"data\": results,\n\t\t\"page\": page,\n\t\t\"limit\": limit,\n\t})\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n main\n\n// cmd/main.go — Fiber router setup\n// Pattern: group-based routing + auth middleware + Swagger protection\n// Observed in: Medical-App-Core/cmd/main.go\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"logistics-api/controllers\"\n\t\"logistics-api/initializers\"\n\t_ \"logistics-api/docs\"\n\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/gofiber/fiber/v2/middleware/basicauth\"\n\t\"github.com/gofiber/fiber/v2/middleware/cors\"\n\t\"github.com/gofiber/fiber/v2/middleware/logger\"\n\t\"github.com/gofiber/swagger\"\n\t\"github.com/joho/godotenv\"\n)\n\n// bearerAuthMiddleware validates the Authorization: Bearer header.\n// Returns 401 if missing or invalid; calls Next() on success.\nfunc bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif len(header) <= 7 || header[:7] != \"Bearer \" {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\t\tif err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\nfunc configureMiddleware(app *fiber.App) {\n\tapp.Use(cors.New(cors.Config{\n\t\tAllowOrigins: \"*\",\n\t\tAllowMethods: \"GET,POST,PUT,PATCH,DELETE,OPTIONS\",\n\t\tAllowHeaders: \"Origin, Content-Type, Accept, Authorization\",\n\t}))\n\tapp.Use(logger.New(logger.Config{\n\t\tFormat: `[$\\{time\\}] $\\{status\\} - $\\{method\\} $\\{path\\}\\n`,\n\t}))\n}\n\nfunc registerRoutes(app *fiber.App, svc *initializers.Services) {\n\tauth := app.Group(\"/auth\")\n\tauth.Post(\"/token\", controllers.NewAuthController(svc.AuthTokenService).GenerateToken)\n\tauth.Post(\"/validate\", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)\n\n\tprotected := app.Group(\"/\", bearerAuthMiddleware(svc))\n\n\tentityCtrl := controllers.NewShipmentController(svc.ShipmentService)\n\tshipments := protected.Group(\"/shipments\")\n\tshipments.Post(\"/\", entityCtrl.CreateShipment)\n\tshipments.Get(\"/\", entityCtrl.ListShipments)\n\tshipments.Get(\"/:tracking_code\", entityCtrl.GetByTrackingCode)\n}\n\nfunc main() {\n\tif err := godotenv.Load(); err != nil {\n\t\tlog.Println(\"warning: .env file not found, using environment\")\n\t}\n\n\tdb := initializers.InitialDB()\n\tinitializers.RunMigrations(db)\n\tsvc := initializers.InitServices(db)\n\n\tapp := fiber.New(fiber.Config{AppName: \"logistics-api v1\"})\n\tconfigureMiddleware(app)\n\n\t// Swagger endpoint protected by basic auth\n\tswaggerUser := os.Getenv(\"SWAGGER_USER\")\n\tswaggerPass := os.Getenv(\"SWAGGER_PASSWORD\")\n\tif swaggerUser == \"\" || swaggerPass == \"\" {\n\t\tlog.Fatal(\"SWAGGER_USER and SWAGGER_PASSWORD must be set\")\n\t}\n\tswaggerAuth := basicauth.New(basicauth.Config{\n\t\tUsers: map[string]string{swaggerUser: swaggerPass},\n\t})\n\tapp.Get(\"/swagger/*\", swaggerAuth, swagger.HandlerDefault)\n\n\tregisterRoutes(app, svc)\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tlog.Printf(\"server listening on :%s\", port)\n\tif err := app.Listen(\":\" + port); err != nil {\n\t\tlog.Fatalf(\"server error: %v\", err)\n\t}\n}\n\n"} {"category": "fiber", "text": "\n go1.24\n utils\n\n// utils/responseUtil.go\n// Pattern: response utility interface + JSON helper\n// Observed in: Medical-App-Core/utils/responseUtil.go\npackage utils\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// ResponseUtil abstracts JSON response construction for testability.\ntype ResponseUtil interface {\n\tRespondWithJSON(c *fiber.Ctx, code int, payload any) error\n\tRespondWithError(c *fiber.Ctx, code int, message string) error\n}\n\n// JSONResponseUtil implements ResponseUtil using Fiber's context.\ntype JSONResponseUtil struct{}\n\nfunc (ru *JSONResponseUtil) RespondWithJSON(c *fiber.Ctx, code int, payload any) error {\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn c.Status(fiber.StatusInternalServerError).\n\t\t\tJSON(fiber.Map{\"error\": \"failed to marshal response\"})\n\t}\n\tc.Set(\"Content-Type\", \"application/json\")\n\treturn c.Status(code).Send(body)\n}\n\nfunc (ru *JSONResponseUtil) RespondWithError(c *fiber.Ctx, code int, message string) error {\n\treturn c.Status(code).JSON(fiber.Map{\"error\": message})\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n entities\n\n// domain/entities/patient.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Patient represents the patients database table.\ntype Patient struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tContact string `gorm:\"type:varchar(50);not null\" json:\"contact\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf,omitempty\"`\n\tSSN string `gorm:\"type:varchar(11);uniqueIndex\" json:\"ssn,omitempty\"`\n\tDOB time.Time `gorm:\"type:date\" json:\"dob\"`\n\tGender string `gorm:\"type:varchar(20)\" json:\"gender,omitempty\"`\n\tAddress string `gorm:\"type:text\" json:\"address,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Patient) TableName() string { return \"patients\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Patient) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n contracts\n\n// domain/repositories/contracts/patientRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// PatientRepository defines the persistence contract for Patient entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype PatientRepository interface {\n\tCreate(dto dtos.PatientDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.PatientDTO, error)\n\tFindByCPF(cpf string) (*dtos.PatientDTO, error)\n\tList(page, limit int) ([]dtos.PatientDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.PatientDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n repositories\n\n// infra/repositories/patientRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// PatientRepositoryImpl implements contracts.PatientRepository using GORM.\ntype PatientRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewPatientRepository constructs a new PatientRepositoryImpl.\nfunc NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {\n\treturn &PatientRepositoryImpl{db: db}\n}\n\nfunc (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {\n\tentity := entities.Patient{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"patient create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.Where(\"cpf = ?\", cpf).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient with cpf=%s not found\", cpf)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {\n\tvar entities []entities.Patient\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"patient list: %w\", err)\n\t}\n\tdtos := make([]dtos.PatientDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {\n\tresult := r.db.Model(&entities.Patient{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Patient{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n entities\n\n// domain/entities/patient.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Patient represents the patients database table.\ntype Patient struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tContact string `gorm:\"type:varchar(50);not null\" json:\"contact\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf,omitempty\"`\n\tSSN string `gorm:\"type:varchar(11);uniqueIndex\" json:\"ssn,omitempty\"`\n\tDOB time.Time `gorm:\"type:date\" json:\"dob\"`\n\tGender string `gorm:\"type:varchar(20)\" json:\"gender,omitempty\"`\n\tAddress string `gorm:\"type:text\" json:\"address,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Patient) TableName() string { return \"patients\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Patient) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n contracts\n\n// domain/repositories/contracts/patientRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// PatientRepository defines the persistence contract for Patient entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype PatientRepository interface {\n\tCreate(dto dtos.PatientDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.PatientDTO, error)\n\tFindByCPF(cpf string) (*dtos.PatientDTO, error)\n\tList(page, limit int) ([]dtos.PatientDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.PatientDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n repositories\n\n// infra/repositories/patientRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// PatientRepositoryImpl implements contracts.PatientRepository using GORM.\ntype PatientRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewPatientRepository constructs a new PatientRepositoryImpl.\nfunc NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {\n\treturn &PatientRepositoryImpl{db: db}\n}\n\nfunc (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {\n\tentity := entities.Patient{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"patient create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.Where(\"cpf = ?\", cpf).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient with cpf=%s not found\", cpf)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {\n\tvar entities []entities.Patient\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"patient list: %w\", err)\n\t}\n\tdtos := make([]dtos.PatientDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {\n\tresult := r.db.Model(&entities.Patient{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Patient{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n entities\n\n// domain/entities/patient.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Patient represents the patients database table.\ntype Patient struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tContact string `gorm:\"type:varchar(50);not null\" json:\"contact\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf,omitempty\"`\n\tSSN string `gorm:\"type:varchar(11);uniqueIndex\" json:\"ssn,omitempty\"`\n\tDOB time.Time `gorm:\"type:date\" json:\"dob\"`\n\tGender string `gorm:\"type:varchar(20)\" json:\"gender,omitempty\"`\n\tAddress string `gorm:\"type:text\" json:\"address,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Patient) TableName() string { return \"patients\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Patient) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n contracts\n\n// domain/repositories/contracts/patientRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// PatientRepository defines the persistence contract for Patient entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype PatientRepository interface {\n\tCreate(dto dtos.PatientDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.PatientDTO, error)\n\tFindByCPF(cpf string) (*dtos.PatientDTO, error)\n\tList(page, limit int) ([]dtos.PatientDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.PatientDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n repositories\n\n// infra/repositories/patientRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// PatientRepositoryImpl implements contracts.PatientRepository using GORM.\ntype PatientRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewPatientRepository constructs a new PatientRepositoryImpl.\nfunc NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {\n\treturn &PatientRepositoryImpl{db: db}\n}\n\nfunc (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {\n\tentity := entities.Patient{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"patient create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.Where(\"cpf = ?\", cpf).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient with cpf=%s not found\", cpf)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {\n\tvar entities []entities.Patient\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"patient list: %w\", err)\n\t}\n\tdtos := make([]dtos.PatientDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {\n\tresult := r.db.Model(&entities.Patient{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Patient{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n entities\n\n// domain/entities/patient.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Patient represents the patients database table.\ntype Patient struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tContact string `gorm:\"type:varchar(50);not null\" json:\"contact\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf,omitempty\"`\n\tSSN string `gorm:\"type:varchar(11);uniqueIndex\" json:\"ssn,omitempty\"`\n\tDOB time.Time `gorm:\"type:date\" json:\"dob\"`\n\tGender string `gorm:\"type:varchar(20)\" json:\"gender,omitempty\"`\n\tAddress string `gorm:\"type:text\" json:\"address,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Patient) TableName() string { return \"patients\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Patient) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n contracts\n\n// domain/repositories/contracts/patientRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// PatientRepository defines the persistence contract for Patient entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype PatientRepository interface {\n\tCreate(dto dtos.PatientDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.PatientDTO, error)\n\tFindByCPF(cpf string) (*dtos.PatientDTO, error)\n\tList(page, limit int) ([]dtos.PatientDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.PatientDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n repositories\n\n// infra/repositories/patientRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// PatientRepositoryImpl implements contracts.PatientRepository using GORM.\ntype PatientRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewPatientRepository constructs a new PatientRepositoryImpl.\nfunc NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {\n\treturn &PatientRepositoryImpl{db: db}\n}\n\nfunc (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {\n\tentity := entities.Patient{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"patient create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {\n\tvar entity entities.Patient\n\tif err := r.db.Where(\"cpf = ?\", cpf).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"patient with cpf=%s not found\", cpf)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\tdto := dtos.PatientDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {\n\tvar entities []entities.Patient\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"patient list: %w\", err)\n\t}\n\tdtos := make([]dtos.PatientDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {\n\tresult := r.db.Model(&entities.Patient{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Patient{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"patient delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"patient not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n entities\n\n// domain/entities/doctor.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Doctor represents the doctors database table.\ntype Doctor struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tFullName string `gorm:\"type:varchar(255);not null\" json:\"full_name\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf\"`\n\tCRM string `gorm:\"type:varchar(20);uniqueIndex\" json:\"crm\"`\n\tSpecialty string `gorm:\"type:varchar(100);not null\" json:\"specialty\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex\" json:\"email\" validate:\"required,email\"`\n\tPhone string `gorm:\"type:varchar(20)\" json:\"phone,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Doctor) TableName() string { return \"doctors\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Doctor) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n contracts\n\n// domain/repositories/contracts/doctorRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// DoctorRepository defines the persistence contract for Doctor entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype DoctorRepository interface {\n\tCreate(dto dtos.DoctorDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.DoctorDTO, error)\n\tFindByCRM(crm string) (*dtos.DoctorDTO, error)\n\tList(page, limit int) ([]dtos.DoctorDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.DoctorDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n repositories\n\n// infra/repositories/doctorRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.\ntype DoctorRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewDoctorRepository constructs a new DoctorRepositoryImpl.\nfunc NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {\n\treturn &DoctorRepositoryImpl{db: db}\n}\n\nfunc (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {\n\tentity := entities.Doctor{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"doctor create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.Where(\"crm = ?\", crm).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor with crm=%s not found\", crm)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {\n\tvar entities []entities.Doctor\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor list: %w\", err)\n\t}\n\tdtos := make([]dtos.DoctorDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {\n\tresult := r.db.Model(&entities.Doctor{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Doctor{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n entities\n\n// domain/entities/doctor.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Doctor represents the doctors database table.\ntype Doctor struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tFullName string `gorm:\"type:varchar(255);not null\" json:\"full_name\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf\"`\n\tCRM string `gorm:\"type:varchar(20);uniqueIndex\" json:\"crm\"`\n\tSpecialty string `gorm:\"type:varchar(100);not null\" json:\"specialty\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex\" json:\"email\" validate:\"required,email\"`\n\tPhone string `gorm:\"type:varchar(20)\" json:\"phone,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Doctor) TableName() string { return \"doctors\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Doctor) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n contracts\n\n// domain/repositories/contracts/doctorRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// DoctorRepository defines the persistence contract for Doctor entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype DoctorRepository interface {\n\tCreate(dto dtos.DoctorDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.DoctorDTO, error)\n\tFindByCRM(crm string) (*dtos.DoctorDTO, error)\n\tList(page, limit int) ([]dtos.DoctorDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.DoctorDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n repositories\n\n// infra/repositories/doctorRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.\ntype DoctorRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewDoctorRepository constructs a new DoctorRepositoryImpl.\nfunc NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {\n\treturn &DoctorRepositoryImpl{db: db}\n}\n\nfunc (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {\n\tentity := entities.Doctor{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"doctor create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.Where(\"crm = ?\", crm).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor with crm=%s not found\", crm)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {\n\tvar entities []entities.Doctor\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor list: %w\", err)\n\t}\n\tdtos := make([]dtos.DoctorDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {\n\tresult := r.db.Model(&entities.Doctor{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Doctor{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n entities\n\n// domain/entities/doctor.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Doctor represents the doctors database table.\ntype Doctor struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tFullName string `gorm:\"type:varchar(255);not null\" json:\"full_name\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf\"`\n\tCRM string `gorm:\"type:varchar(20);uniqueIndex\" json:\"crm\"`\n\tSpecialty string `gorm:\"type:varchar(100);not null\" json:\"specialty\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex\" json:\"email\" validate:\"required,email\"`\n\tPhone string `gorm:\"type:varchar(20)\" json:\"phone,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Doctor) TableName() string { return \"doctors\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Doctor) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n contracts\n\n// domain/repositories/contracts/doctorRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// DoctorRepository defines the persistence contract for Doctor entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype DoctorRepository interface {\n\tCreate(dto dtos.DoctorDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.DoctorDTO, error)\n\tFindByCRM(crm string) (*dtos.DoctorDTO, error)\n\tList(page, limit int) ([]dtos.DoctorDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.DoctorDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n repositories\n\n// infra/repositories/doctorRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.\ntype DoctorRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewDoctorRepository constructs a new DoctorRepositoryImpl.\nfunc NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {\n\treturn &DoctorRepositoryImpl{db: db}\n}\n\nfunc (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {\n\tentity := entities.Doctor{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"doctor create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.Where(\"crm = ?\", crm).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor with crm=%s not found\", crm)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {\n\tvar entities []entities.Doctor\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor list: %w\", err)\n\t}\n\tdtos := make([]dtos.DoctorDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {\n\tresult := r.db.Model(&entities.Doctor{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Doctor{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n entities\n\n// domain/entities/doctor.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Doctor represents the doctors database table.\ntype Doctor struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tFullName string `gorm:\"type:varchar(255);not null\" json:\"full_name\" validate:\"required\"`\n\tCPF string `gorm:\"type:varchar(14);uniqueIndex\" json:\"cpf\"`\n\tCRM string `gorm:\"type:varchar(20);uniqueIndex\" json:\"crm\"`\n\tSpecialty string `gorm:\"type:varchar(100);not null\" json:\"specialty\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex\" json:\"email\" validate:\"required,email\"`\n\tPhone string `gorm:\"type:varchar(20)\" json:\"phone,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Doctor) TableName() string { return \"doctors\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Doctor) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n contracts\n\n// domain/repositories/contracts/doctorRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// DoctorRepository defines the persistence contract for Doctor entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype DoctorRepository interface {\n\tCreate(dto dtos.DoctorDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.DoctorDTO, error)\n\tFindByCRM(crm string) (*dtos.DoctorDTO, error)\n\tList(page, limit int) ([]dtos.DoctorDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.DoctorDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n repositories\n\n// infra/repositories/doctorRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.\ntype DoctorRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewDoctorRepository constructs a new DoctorRepositoryImpl.\nfunc NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {\n\treturn &DoctorRepositoryImpl{db: db}\n}\n\nfunc (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {\n\tentity := entities.Doctor{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"doctor create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {\n\tvar entity entities.Doctor\n\tif err := r.db.Where(\"crm = ?\", crm).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"doctor with crm=%s not found\", crm)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\tdto := dtos.DoctorDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {\n\tvar entities []entities.Doctor\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor list: %w\", err)\n\t}\n\tdtos := make([]dtos.DoctorDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {\n\tresult := r.db.Model(&entities.Doctor{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Doctor{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"doctor delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"doctor not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n entities\n\n// domain/entities/appointment.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Appointment represents the appointments database table.\ntype Appointment struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tPatientID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"patient_id\"`\n\tDoctorID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"doctor_id\"`\n\tUserID uuid.UUID `gorm:\"type:uuid;not null\" json:\"user_id\"`\n\tSpecialization string `gorm:\"type:varchar(100);not null\" json:\"specialization\"`\n\tDateTime time.Time `gorm:\"type:timestamptz;not null\" json:\"date_time\"`\n\tStatus string `gorm:\"type:varchar(50);default:scheduled\" json:\"status\"`\n\tNotes string `gorm:\"type:text\" json:\"notes,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Appointment) TableName() string { return \"appointments\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Appointment) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n contracts\n\n// domain/repositories/contracts/appointmentRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// AppointmentRepository defines the persistence contract for Appointment entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype AppointmentRepository interface {\n\tCreate(dto dtos.AppointmentDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)\n\tFindByStatus(status string) (*dtos.AppointmentDTO, error)\n\tList(page, limit int) ([]dtos.AppointmentDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.AppointmentDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n repositories\n\n// infra/repositories/appointmentRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.\ntype AppointmentRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.\nfunc NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {\n\treturn &AppointmentRepositoryImpl{db: db}\n}\n\nfunc (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {\n\tentity := entities.Appointment{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"appointment create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.Where(\"status = ?\", status).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment with status=%s not found\", status)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {\n\tvar entities []entities.Appointment\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment list: %w\", err)\n\t}\n\tdtos := make([]dtos.AppointmentDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {\n\tresult := r.db.Model(&entities.Appointment{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Appointment{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n entities\n\n// domain/entities/appointment.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Appointment represents the appointments database table.\ntype Appointment struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tPatientID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"patient_id\"`\n\tDoctorID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"doctor_id\"`\n\tUserID uuid.UUID `gorm:\"type:uuid;not null\" json:\"user_id\"`\n\tSpecialization string `gorm:\"type:varchar(100);not null\" json:\"specialization\"`\n\tDateTime time.Time `gorm:\"type:timestamptz;not null\" json:\"date_time\"`\n\tStatus string `gorm:\"type:varchar(50);default:scheduled\" json:\"status\"`\n\tNotes string `gorm:\"type:text\" json:\"notes,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Appointment) TableName() string { return \"appointments\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Appointment) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n contracts\n\n// domain/repositories/contracts/appointmentRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// AppointmentRepository defines the persistence contract for Appointment entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype AppointmentRepository interface {\n\tCreate(dto dtos.AppointmentDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)\n\tFindByStatus(status string) (*dtos.AppointmentDTO, error)\n\tList(page, limit int) ([]dtos.AppointmentDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.AppointmentDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n repositories\n\n// infra/repositories/appointmentRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.\ntype AppointmentRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.\nfunc NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {\n\treturn &AppointmentRepositoryImpl{db: db}\n}\n\nfunc (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {\n\tentity := entities.Appointment{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"appointment create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.Where(\"status = ?\", status).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment with status=%s not found\", status)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {\n\tvar entities []entities.Appointment\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment list: %w\", err)\n\t}\n\tdtos := make([]dtos.AppointmentDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {\n\tresult := r.db.Model(&entities.Appointment{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Appointment{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n entities\n\n// domain/entities/appointment.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Appointment represents the appointments database table.\ntype Appointment struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tPatientID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"patient_id\"`\n\tDoctorID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"doctor_id\"`\n\tUserID uuid.UUID `gorm:\"type:uuid;not null\" json:\"user_id\"`\n\tSpecialization string `gorm:\"type:varchar(100);not null\" json:\"specialization\"`\n\tDateTime time.Time `gorm:\"type:timestamptz;not null\" json:\"date_time\"`\n\tStatus string `gorm:\"type:varchar(50);default:scheduled\" json:\"status\"`\n\tNotes string `gorm:\"type:text\" json:\"notes,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Appointment) TableName() string { return \"appointments\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Appointment) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n contracts\n\n// domain/repositories/contracts/appointmentRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// AppointmentRepository defines the persistence contract for Appointment entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype AppointmentRepository interface {\n\tCreate(dto dtos.AppointmentDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)\n\tFindByStatus(status string) (*dtos.AppointmentDTO, error)\n\tList(page, limit int) ([]dtos.AppointmentDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.AppointmentDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n repositories\n\n// infra/repositories/appointmentRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.\ntype AppointmentRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.\nfunc NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {\n\treturn &AppointmentRepositoryImpl{db: db}\n}\n\nfunc (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {\n\tentity := entities.Appointment{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"appointment create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.Where(\"status = ?\", status).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment with status=%s not found\", status)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {\n\tvar entities []entities.Appointment\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment list: %w\", err)\n\t}\n\tdtos := make([]dtos.AppointmentDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {\n\tresult := r.db.Model(&entities.Appointment{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Appointment{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n entities\n\n// domain/entities/appointment.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// Appointment represents the appointments database table.\ntype Appointment struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tPatientID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"patient_id\"`\n\tDoctorID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"doctor_id\"`\n\tUserID uuid.UUID `gorm:\"type:uuid;not null\" json:\"user_id\"`\n\tSpecialization string `gorm:\"type:varchar(100);not null\" json:\"specialization\"`\n\tDateTime time.Time `gorm:\"type:timestamptz;not null\" json:\"date_time\"`\n\tStatus string `gorm:\"type:varchar(50);default:scheduled\" json:\"status\"`\n\tNotes string `gorm:\"type:text\" json:\"notes,omitempty\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (Appointment) TableName() string { return \"appointments\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *Appointment) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n contracts\n\n// domain/repositories/contracts/appointmentRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// AppointmentRepository defines the persistence contract for Appointment entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype AppointmentRepository interface {\n\tCreate(dto dtos.AppointmentDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)\n\tFindByStatus(status string) (*dtos.AppointmentDTO, error)\n\tList(page, limit int) ([]dtos.AppointmentDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.AppointmentDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n repositories\n\n// infra/repositories/appointmentRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.\ntype AppointmentRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.\nfunc NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {\n\treturn &AppointmentRepositoryImpl{db: db}\n}\n\nfunc (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {\n\tentity := entities.Appointment{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"appointment create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {\n\tvar entity entities.Appointment\n\tif err := r.db.Where(\"status = ?\", status).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"appointment with status=%s not found\", status)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\tdto := dtos.AppointmentDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {\n\tvar entities []entities.Appointment\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment list: %w\", err)\n\t}\n\tdtos := make([]dtos.AppointmentDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {\n\tresult := r.db.Model(&entities.Appointment{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.Appointment{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"appointment delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"appointment not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n entities\n\n// domain/entities/user.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// User represents the users database table.\ntype User struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex;not null\" json:\"email\" validate:\"required,email\"`\n\tPassword string `gorm:\"type:varchar(255);not null\" json:\"-\"` // never serialised\n\tRole string `gorm:\"type:varchar(50);not null;default:user\" json:\"role\" validate:\"required,oneof=admin user doctor nurse\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (User) TableName() string { return \"users\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *User) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n contracts\n\n// domain/repositories/contracts/userRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// UserRepository defines the persistence contract for User entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype UserRepository interface {\n\tCreate(dto dtos.UserDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.UserDTO, error)\n\tFindByEmail(email string) (*dtos.UserDTO, error)\n\tList(page, limit int) ([]dtos.UserDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.UserDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n repositories\n\n// infra/repositories/userRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// UserRepositoryImpl implements contracts.UserRepository using GORM.\ntype UserRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewUserRepository constructs a new UserRepositoryImpl.\nfunc NewUserRepository(db *gorm.DB) *UserRepositoryImpl {\n\treturn &UserRepositoryImpl{db: db}\n}\n\nfunc (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {\n\tentity := entities.User{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"user create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find: %w\", err)\n\t}\n\tdto := dtos.UserDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.Where(\"email = ?\", email).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user with email=%s not found\", email)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find by email: %w\", err)\n\t}\n\tdto := dtos.UserDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {\n\tvar entities []entities.User\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"user list: %w\", err)\n\t}\n\tdtos := make([]dtos.UserDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {\n\tresult := r.db.Model(&entities.User{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *UserRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.User{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n entities\n\n// domain/entities/user.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// User represents the users database table.\ntype User struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex;not null\" json:\"email\" validate:\"required,email\"`\n\tPassword string `gorm:\"type:varchar(255);not null\" json:\"-\"` // never serialised\n\tRole string `gorm:\"type:varchar(50);not null;default:user\" json:\"role\" validate:\"required,oneof=admin user doctor nurse\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (User) TableName() string { return \"users\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *User) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n contracts\n\n// domain/repositories/contracts/userRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// UserRepository defines the persistence contract for User entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype UserRepository interface {\n\tCreate(dto dtos.UserDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.UserDTO, error)\n\tFindByEmail(email string) (*dtos.UserDTO, error)\n\tList(page, limit int) ([]dtos.UserDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.UserDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n repositories\n\n// infra/repositories/userRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// UserRepositoryImpl implements contracts.UserRepository using GORM.\ntype UserRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewUserRepository constructs a new UserRepositoryImpl.\nfunc NewUserRepository(db *gorm.DB) *UserRepositoryImpl {\n\treturn &UserRepositoryImpl{db: db}\n}\n\nfunc (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {\n\tentity := entities.User{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"user create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find: %w\", err)\n\t}\n\tdto := dtos.UserDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.Where(\"email = ?\", email).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user with email=%s not found\", email)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find by email: %w\", err)\n\t}\n\tdto := dtos.UserDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {\n\tvar entities []entities.User\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"user list: %w\", err)\n\t}\n\tdtos := make([]dtos.UserDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {\n\tresult := r.db.Model(&entities.User{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *UserRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.User{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n entities\n\n// domain/entities/user.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// User represents the users database table.\ntype User struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex;not null\" json:\"email\" validate:\"required,email\"`\n\tPassword string `gorm:\"type:varchar(255);not null\" json:\"-\"` // never serialised\n\tRole string `gorm:\"type:varchar(50);not null;default:user\" json:\"role\" validate:\"required,oneof=admin user doctor nurse\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (User) TableName() string { return \"users\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *User) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n contracts\n\n// domain/repositories/contracts/userRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// UserRepository defines the persistence contract for User entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype UserRepository interface {\n\tCreate(dto dtos.UserDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.UserDTO, error)\n\tFindByEmail(email string) (*dtos.UserDTO, error)\n\tList(page, limit int) ([]dtos.UserDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.UserDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n repositories\n\n// infra/repositories/userRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// UserRepositoryImpl implements contracts.UserRepository using GORM.\ntype UserRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewUserRepository constructs a new UserRepositoryImpl.\nfunc NewUserRepository(db *gorm.DB) *UserRepositoryImpl {\n\treturn &UserRepositoryImpl{db: db}\n}\n\nfunc (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {\n\tentity := entities.User{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"user create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find: %w\", err)\n\t}\n\tdto := dtos.UserDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.Where(\"email = ?\", email).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user with email=%s not found\", email)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find by email: %w\", err)\n\t}\n\tdto := dtos.UserDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {\n\tvar entities []entities.User\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"user list: %w\", err)\n\t}\n\tdtos := make([]dtos.UserDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {\n\tresult := r.db.Model(&entities.User{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *UserRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.User{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n entities\n\n// domain/entities/user.go\n// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags\n// Observed in: Medical-App-Core/domain/entities/\npackage entities\n\nimport (\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// User represents the users database table.\ntype User struct {\n\tID uuid.UUID `gorm:\"type:uuid;primaryKey;default:gen_random_uuid()\" json:\"id\"`\n\tCreatedAt time.Time `gorm:\"autoCreateTime\" json:\"created_at\"`\n\tUpdatedAt time.Time `gorm:\"autoUpdateTime\" json:\"updated_at\"`\n\tDeletedAt gorm.DeletedAt `gorm:\"index\" json:\"-\"` // soft delete\n\tOrganizationID uuid.UUID `gorm:\"type:uuid;not null;index\" json:\"organization_id\"`\n\tName string `gorm:\"type:varchar(255);not null\" json:\"name\" validate:\"required\"`\n\tEmail string `gorm:\"type:varchar(255);uniqueIndex;not null\" json:\"email\" validate:\"required,email\"`\n\tPassword string `gorm:\"type:varchar(255);not null\" json:\"-\"` // never serialised\n\tRole string `gorm:\"type:varchar(50);not null;default:user\" json:\"role\" validate:\"required,oneof=admin user doctor nurse\"`\n}\n\n// TableName overrides GORM's convention-based table name.\nfunc (User) TableName() string { return \"users\" }\n\n// BeforeCreate sets a UUID before the record is inserted.\nfunc (e *User) BeforeCreate(tx *gorm.DB) error {\n\tif e.ID == uuid.Nil {\n\t\te.ID = uuid.New()\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n contracts\n\n// domain/repositories/contracts/userRepository.go\n// Pattern: repository interface (contract) — domain layer knows nothing about GORM\n// Observed in: Medical-App-Core/domain/repositories/contracts/\npackage contracts\n\nimport (\n\t\"medical-sas-api/domain/dtos\"\n\t\"github.com/google/uuid\"\n)\n\n// UserRepository defines the persistence contract for User entities.\n// Implementations live in infra/repositories/ and depend on GORM or any other ORM.\ntype UserRepository interface {\n\tCreate(dto dtos.UserDTO) (uuid.UUID, error)\n\tFindByID(id uuid.UUID) (*dtos.UserDTO, error)\n\tFindByEmail(email string) (*dtos.UserDTO, error)\n\tList(page, limit int) ([]dtos.UserDTO, error)\n\tUpdate(id uuid.UUID, dto dtos.UserDTO) error\n\tDelete(id uuid.UUID) error // soft delete\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n repositories\n\n// infra/repositories/userRepository.go\n// Pattern: GORM implementation of the repository interface\n// Observed in: Medical-App-Core/infra/repositories/\npackage repositories\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/dtos\"\n\t\"medical-sas-api/domain/entities\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\n// UserRepositoryImpl implements contracts.UserRepository using GORM.\ntype UserRepositoryImpl struct {\n\tdb *gorm.DB\n}\n\n// NewUserRepository constructs a new UserRepositoryImpl.\nfunc NewUserRepository(db *gorm.DB) *UserRepositoryImpl {\n\treturn &UserRepositoryImpl{db: db}\n}\n\nfunc (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {\n\tentity := entities.User{} // map DTO → entity fields\n\t// (field mapping omitted for brevity — use a mapper or manual assignment)\n\tif err := r.db.Create(&entity).Error; err != nil {\n\t\treturn uuid.Nil, fmt.Errorf(\"user create: %w\", err)\n\t}\n\treturn entity.ID, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.First(&entity, \"id = ?\", id).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user not found: %s\", id)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find: %w\", err)\n\t}\n\tdto := dtos.UserDTO{} // map entity → DTO\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {\n\tvar entity entities.User\n\tif err := r.db.Where(\"email = ?\", email).First(&entity).Error; err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, fmt.Errorf(\"user with email=%s not found\", email)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"user find by email: %w\", err)\n\t}\n\tdto := dtos.UserDTO{}\n\treturn &dto, nil\n}\n\nfunc (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {\n\tvar entities []entities.User\n\toffset := (page - 1) * limit\n\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"user list: %w\", err)\n\t}\n\tdtos := make([]dtos.UserDTO, 0, len(entities))\n\t// map each entity to DTO\n\treturn dtos, nil\n}\n\nfunc (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {\n\tresult := r.db.Model(&entities.User{}).Where(\"id = ?\", id).Updates(dto)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user update: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\nfunc (r *UserRepositoryImpl) Delete(id uuid.UUID) error {\n\t// GORM soft delete: sets deleted_at instead of removing the row\n\tresult := r.db.Delete(&entities.User{}, \"id = ?\", id)\n\tif result.Error != nil {\n\t\treturn fmt.Errorf(\"user delete: %w\", result.Error)\n\t}\n\tif result.RowsAffected == 0 {\n\t\treturn fmt.Errorf(\"user not found: %s\", id)\n\t}\n\treturn nil\n}\n\n"} {"category": "gorm", "text": "\n go1.21\n initializers\n\n// initializers/database.go\n// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN\n// Observed in: Medical-App-Core/initializers/database.go\npackage initializers\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\n// InitialDB opens a PostgreSQL connection using environment variables.\n// Panics on failure — the app cannot run without a database.\nfunc InitialDB() *gorm.DB {\n\tdsn := buildDSN()\n\n\tdb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Info),\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"database connection failed: %v\", err)\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to get underlying sql.DB: %v\", err)\n\t}\n\tsqlDB.SetMaxOpenConns(25)\n\tsqlDB.SetMaxIdleConns(10)\n\tsqlDB.SetConnMaxLifetime(5 * time.Minute)\n\n\tlog.Println(\"database connected:\", maskDSN(dsn))\n\treturn db\n}\n\nfunc buildDSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s\",\n\t\tgetEnv(\"DB_HOST\", \"localhost\"),\n\t\tgetEnv(\"DB_PORT\", \"5432\"),\n\t\tgetEnv(\"DB_USER\", \"postgres\"),\n\t\tos.Getenv(\"DB_PASSWORD\"),\n\t\tgetEnv(\"DB_NAME\", \"appdb\"),\n\t\tgetEnv(\"DB_SSLMODE\", \"disable\"),\n\t\tgetEnv(\"DB_TIMEZONE\", \"UTC\"),\n\t)\n}\n\nfunc getEnv(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc maskDSN(dsn string) string {\n\tpassword := os.Getenv(\"DB_PASSWORD\")\n\tif password == \"\" {\n\t\treturn dsn\n\t}\n\treturn strings.ReplaceAll(dsn, password, \"*****\")\n}\n\n"} {"category": "gorm", "text": "\n go1.22\n initializers\n\n// initializers/database.go\n// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN\n// Observed in: Medical-App-Core/initializers/database.go\npackage initializers\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\n// InitialDB opens a PostgreSQL connection using environment variables.\n// Panics on failure — the app cannot run without a database.\nfunc InitialDB() *gorm.DB {\n\tdsn := buildDSN()\n\n\tdb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Info),\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"database connection failed: %v\", err)\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to get underlying sql.DB: %v\", err)\n\t}\n\tsqlDB.SetMaxOpenConns(25)\n\tsqlDB.SetMaxIdleConns(10)\n\tsqlDB.SetConnMaxLifetime(5 * time.Minute)\n\n\tlog.Println(\"database connected:\", maskDSN(dsn))\n\treturn db\n}\n\nfunc buildDSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s\",\n\t\tgetEnv(\"DB_HOST\", \"localhost\"),\n\t\tgetEnv(\"DB_PORT\", \"5432\"),\n\t\tgetEnv(\"DB_USER\", \"postgres\"),\n\t\tos.Getenv(\"DB_PASSWORD\"),\n\t\tgetEnv(\"DB_NAME\", \"appdb\"),\n\t\tgetEnv(\"DB_SSLMODE\", \"disable\"),\n\t\tgetEnv(\"DB_TIMEZONE\", \"UTC\"),\n\t)\n}\n\nfunc getEnv(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc maskDSN(dsn string) string {\n\tpassword := os.Getenv(\"DB_PASSWORD\")\n\tif password == \"\" {\n\t\treturn dsn\n\t}\n\treturn strings.ReplaceAll(dsn, password, \"*****\")\n}\n\n"} {"category": "gorm", "text": "\n go1.23\n initializers\n\n// initializers/database.go\n// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN\n// Observed in: Medical-App-Core/initializers/database.go\npackage initializers\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\n// InitialDB opens a PostgreSQL connection using environment variables.\n// Panics on failure — the app cannot run without a database.\nfunc InitialDB() *gorm.DB {\n\tdsn := buildDSN()\n\n\tdb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Info),\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"database connection failed: %v\", err)\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to get underlying sql.DB: %v\", err)\n\t}\n\tsqlDB.SetMaxOpenConns(25)\n\tsqlDB.SetMaxIdleConns(10)\n\tsqlDB.SetConnMaxLifetime(5 * time.Minute)\n\n\tlog.Println(\"database connected:\", maskDSN(dsn))\n\treturn db\n}\n\nfunc buildDSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s\",\n\t\tgetEnv(\"DB_HOST\", \"localhost\"),\n\t\tgetEnv(\"DB_PORT\", \"5432\"),\n\t\tgetEnv(\"DB_USER\", \"postgres\"),\n\t\tos.Getenv(\"DB_PASSWORD\"),\n\t\tgetEnv(\"DB_NAME\", \"appdb\"),\n\t\tgetEnv(\"DB_SSLMODE\", \"disable\"),\n\t\tgetEnv(\"DB_TIMEZONE\", \"UTC\"),\n\t)\n}\n\nfunc getEnv(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc maskDSN(dsn string) string {\n\tpassword := os.Getenv(\"DB_PASSWORD\")\n\tif password == \"\" {\n\t\treturn dsn\n\t}\n\treturn strings.ReplaceAll(dsn, password, \"*****\")\n}\n\n"} {"category": "gorm", "text": "\n go1.24\n initializers\n\n// initializers/database.go\n// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN\n// Observed in: Medical-App-Core/initializers/database.go\npackage initializers\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\n// InitialDB opens a PostgreSQL connection using environment variables.\n// Panics on failure — the app cannot run without a database.\nfunc InitialDB() *gorm.DB {\n\tdsn := buildDSN()\n\n\tdb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Info),\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"database connection failed: %v\", err)\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to get underlying sql.DB: %v\", err)\n\t}\n\tsqlDB.SetMaxOpenConns(25)\n\tsqlDB.SetMaxIdleConns(10)\n\tsqlDB.SetConnMaxLifetime(5 * time.Minute)\n\n\tlog.Println(\"database connected:\", maskDSN(dsn))\n\treturn db\n}\n\nfunc buildDSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s\",\n\t\tgetEnv(\"DB_HOST\", \"localhost\"),\n\t\tgetEnv(\"DB_PORT\", \"5432\"),\n\t\tgetEnv(\"DB_USER\", \"postgres\"),\n\t\tos.Getenv(\"DB_PASSWORD\"),\n\t\tgetEnv(\"DB_NAME\", \"appdb\"),\n\t\tgetEnv(\"DB_SSLMODE\", \"disable\"),\n\t\tgetEnv(\"DB_TIMEZONE\", \"UTC\"),\n\t)\n}\n\nfunc getEnv(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc maskDSN(dsn string) string {\n\tpassword := os.Getenv(\"DB_PASSWORD\")\n\tif password == \"\" {\n\t\treturn dsn\n\t}\n\treturn strings.ReplaceAll(dsn, password, \"*****\")\n}\n\n"} {"category": "service", "text": "\n go1.21\n contracts\n\n// services/contracts/patientServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// PatientServiceContract is the interface every Patient service implementation must satisfy.\ntype PatientServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input PatientInputDTO) (uuid.UUID, error)\n\n\tFindByCPF(cpf string) (*PatientDTO, error)\n\n\tList(page, limit int) ([]PatientDTO, error)\n\n\tUpdate(id uuid.UUID, dto PatientDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// PatientInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype PatientInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n// PatientDTO is the canonical data transfer object passed between layers.\ntype PatientDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n"} {"category": "service", "text": "\n go1.21\n implementations\n\n// services/implementations/patientService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// PatientService implements sc.PatientServiceContract.\ntype PatientService struct {\n\trepo contracts.PatientRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewPatientService constructs a PatientService with all required repositories.\nfunc NewPatientService(\n\trepo contracts.PatientRepository,\n\torgRepo contracts.OrganizationRepository,\n) *PatientService {\n\treturn &PatientService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.PatientDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {\n\tresult, err := s.repo.FindByCPF(cpf)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *PatientService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.22\n contracts\n\n// services/contracts/patientServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// PatientServiceContract is the interface every Patient service implementation must satisfy.\ntype PatientServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input PatientInputDTO) (uuid.UUID, error)\n\n\tFindByCPF(cpf string) (*PatientDTO, error)\n\n\tList(page, limit int) ([]PatientDTO, error)\n\n\tUpdate(id uuid.UUID, dto PatientDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// PatientInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype PatientInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n// PatientDTO is the canonical data transfer object passed between layers.\ntype PatientDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n"} {"category": "service", "text": "\n go1.22\n implementations\n\n// services/implementations/patientService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// PatientService implements sc.PatientServiceContract.\ntype PatientService struct {\n\trepo contracts.PatientRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewPatientService constructs a PatientService with all required repositories.\nfunc NewPatientService(\n\trepo contracts.PatientRepository,\n\torgRepo contracts.OrganizationRepository,\n) *PatientService {\n\treturn &PatientService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.PatientDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {\n\tresult, err := s.repo.FindByCPF(cpf)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *PatientService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.23\n contracts\n\n// services/contracts/patientServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// PatientServiceContract is the interface every Patient service implementation must satisfy.\ntype PatientServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input PatientInputDTO) (uuid.UUID, error)\n\n\tFindByCPF(cpf string) (*PatientDTO, error)\n\n\tList(page, limit int) ([]PatientDTO, error)\n\n\tUpdate(id uuid.UUID, dto PatientDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// PatientInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype PatientInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n// PatientDTO is the canonical data transfer object passed between layers.\ntype PatientDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n"} {"category": "service", "text": "\n go1.23\n implementations\n\n// services/implementations/patientService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// PatientService implements sc.PatientServiceContract.\ntype PatientService struct {\n\trepo contracts.PatientRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewPatientService constructs a PatientService with all required repositories.\nfunc NewPatientService(\n\trepo contracts.PatientRepository,\n\torgRepo contracts.OrganizationRepository,\n) *PatientService {\n\treturn &PatientService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.PatientDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {\n\tresult, err := s.repo.FindByCPF(cpf)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *PatientService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.24\n contracts\n\n// services/contracts/patientServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// PatientServiceContract is the interface every Patient service implementation must satisfy.\ntype PatientServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input PatientInputDTO) (uuid.UUID, error)\n\n\tFindByCPF(cpf string) (*PatientDTO, error)\n\n\tList(page, limit int) ([]PatientDTO, error)\n\n\tUpdate(id uuid.UUID, dto PatientDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// PatientInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype PatientInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n// PatientDTO is the canonical data transfer object passed between layers.\ntype PatientDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tName string `json:\"name\"`\n\tContact string `json:\"contact\"`\n\tCPF string `json:\"cpf,omitempty\"`\n}\n\n"} {"category": "service", "text": "\n go1.24\n implementations\n\n// services/implementations/patientService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// PatientService implements sc.PatientServiceContract.\ntype PatientService struct {\n\trepo contracts.PatientRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewPatientService constructs a PatientService with all required repositories.\nfunc NewPatientService(\n\trepo contracts.PatientRepository,\n\torgRepo contracts.OrganizationRepository,\n) *PatientService {\n\treturn &PatientService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.PatientDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {\n\tresult, err := s.repo.FindByCPF(cpf)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"patient find by cpf: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *PatientService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.21\n contracts\n\n// services/contracts/doctorServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// DoctorServiceContract is the interface every Doctor service implementation must satisfy.\ntype DoctorServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input DoctorInputDTO) (uuid.UUID, error)\n\n\tFindByCRM(crm string) (*DoctorDTO, error)\n\n\tList(page, limit int) ([]DoctorDTO, error)\n\n\tUpdate(id uuid.UUID, dto DoctorDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// DoctorInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype DoctorInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tFullName string `json:\"full_name\"`\n\tCPF string `json:\"cpf\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n// DoctorDTO is the canonical data transfer object passed between layers.\ntype DoctorDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tFullName string `json:\"full_name\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n"} {"category": "service", "text": "\n go1.21\n implementations\n\n// services/implementations/doctorService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// DoctorService implements sc.DoctorServiceContract.\ntype DoctorService struct {\n\trepo contracts.DoctorRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewDoctorService constructs a DoctorService with all required repositories.\nfunc NewDoctorService(\n\trepo contracts.DoctorRepository,\n\torgRepo contracts.OrganizationRepository,\n) *DoctorService {\n\treturn &DoctorService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.DoctorDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {\n\tresult, err := s.repo.FindByCRM(crm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *DoctorService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.22\n contracts\n\n// services/contracts/doctorServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// DoctorServiceContract is the interface every Doctor service implementation must satisfy.\ntype DoctorServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input DoctorInputDTO) (uuid.UUID, error)\n\n\tFindByCRM(crm string) (*DoctorDTO, error)\n\n\tList(page, limit int) ([]DoctorDTO, error)\n\n\tUpdate(id uuid.UUID, dto DoctorDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// DoctorInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype DoctorInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tFullName string `json:\"full_name\"`\n\tCPF string `json:\"cpf\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n// DoctorDTO is the canonical data transfer object passed between layers.\ntype DoctorDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tFullName string `json:\"full_name\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n"} {"category": "service", "text": "\n go1.22\n implementations\n\n// services/implementations/doctorService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// DoctorService implements sc.DoctorServiceContract.\ntype DoctorService struct {\n\trepo contracts.DoctorRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewDoctorService constructs a DoctorService with all required repositories.\nfunc NewDoctorService(\n\trepo contracts.DoctorRepository,\n\torgRepo contracts.OrganizationRepository,\n) *DoctorService {\n\treturn &DoctorService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.DoctorDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {\n\tresult, err := s.repo.FindByCRM(crm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *DoctorService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.23\n contracts\n\n// services/contracts/doctorServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// DoctorServiceContract is the interface every Doctor service implementation must satisfy.\ntype DoctorServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input DoctorInputDTO) (uuid.UUID, error)\n\n\tFindByCRM(crm string) (*DoctorDTO, error)\n\n\tList(page, limit int) ([]DoctorDTO, error)\n\n\tUpdate(id uuid.UUID, dto DoctorDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// DoctorInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype DoctorInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tFullName string `json:\"full_name\"`\n\tCPF string `json:\"cpf\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n// DoctorDTO is the canonical data transfer object passed between layers.\ntype DoctorDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tFullName string `json:\"full_name\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n"} {"category": "service", "text": "\n go1.23\n implementations\n\n// services/implementations/doctorService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// DoctorService implements sc.DoctorServiceContract.\ntype DoctorService struct {\n\trepo contracts.DoctorRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewDoctorService constructs a DoctorService with all required repositories.\nfunc NewDoctorService(\n\trepo contracts.DoctorRepository,\n\torgRepo contracts.OrganizationRepository,\n) *DoctorService {\n\treturn &DoctorService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.DoctorDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {\n\tresult, err := s.repo.FindByCRM(crm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *DoctorService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.24\n contracts\n\n// services/contracts/doctorServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// DoctorServiceContract is the interface every Doctor service implementation must satisfy.\ntype DoctorServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input DoctorInputDTO) (uuid.UUID, error)\n\n\tFindByCRM(crm string) (*DoctorDTO, error)\n\n\tList(page, limit int) ([]DoctorDTO, error)\n\n\tUpdate(id uuid.UUID, dto DoctorDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// DoctorInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype DoctorInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tFullName string `json:\"full_name\"`\n\tCPF string `json:\"cpf\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n// DoctorDTO is the canonical data transfer object passed between layers.\ntype DoctorDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tFullName string `json:\"full_name\"`\n\tCRM string `json:\"crm\"`\n\tSpecialty string `json:\"specialty\"`\n}\n\n"} {"category": "service", "text": "\n go1.24\n implementations\n\n// services/implementations/doctorService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// DoctorService implements sc.DoctorServiceContract.\ntype DoctorService struct {\n\trepo contracts.DoctorRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewDoctorService constructs a DoctorService with all required repositories.\nfunc NewDoctorService(\n\trepo contracts.DoctorRepository,\n\torgRepo contracts.OrganizationRepository,\n) *DoctorService {\n\treturn &DoctorService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.DoctorDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {\n\tresult, err := s.repo.FindByCRM(crm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"doctor find by crm: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *DoctorService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.21\n contracts\n\n// services/contracts/appointmentServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.\ntype AppointmentServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)\n\n\tFindByStatus(status string) (*AppointmentDTO, error)\n\n\tList(page, limit int) ([]AppointmentDTO, error)\n\n\tUpdate(id uuid.UUID, dto AppointmentDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// AppointmentInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype AppointmentInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tPatientName string `json:\"patient_name\"`\n\tDoctorFullName string `json:\"doctor_full_name\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n}\n\n// AppointmentDTO is the canonical data transfer object passed between layers.\ntype AppointmentDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tPatientID string `json:\"patient_id\"`\n\tDoctorID string `json:\"doctor_id\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n\tStatus string `json:\"status\"`\n}\n\n"} {"category": "service", "text": "\n go1.21\n implementations\n\n// services/implementations/appointmentService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// AppointmentService implements sc.AppointmentServiceContract.\ntype AppointmentService struct {\n\trepo contracts.AppointmentRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewAppointmentService constructs a AppointmentService with all required repositories.\nfunc NewAppointmentService(\n\trepo contracts.AppointmentRepository,\n\torgRepo contracts.OrganizationRepository,\n) *AppointmentService {\n\treturn &AppointmentService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.AppointmentDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {\n\tresult, err := s.repo.FindByStatus(status)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *AppointmentService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.22\n contracts\n\n// services/contracts/appointmentServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.\ntype AppointmentServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)\n\n\tFindByStatus(status string) (*AppointmentDTO, error)\n\n\tList(page, limit int) ([]AppointmentDTO, error)\n\n\tUpdate(id uuid.UUID, dto AppointmentDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// AppointmentInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype AppointmentInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tPatientName string `json:\"patient_name\"`\n\tDoctorFullName string `json:\"doctor_full_name\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n}\n\n// AppointmentDTO is the canonical data transfer object passed between layers.\ntype AppointmentDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tPatientID string `json:\"patient_id\"`\n\tDoctorID string `json:\"doctor_id\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n\tStatus string `json:\"status\"`\n}\n\n"} {"category": "service", "text": "\n go1.22\n implementations\n\n// services/implementations/appointmentService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// AppointmentService implements sc.AppointmentServiceContract.\ntype AppointmentService struct {\n\trepo contracts.AppointmentRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewAppointmentService constructs a AppointmentService with all required repositories.\nfunc NewAppointmentService(\n\trepo contracts.AppointmentRepository,\n\torgRepo contracts.OrganizationRepository,\n) *AppointmentService {\n\treturn &AppointmentService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.AppointmentDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {\n\tresult, err := s.repo.FindByStatus(status)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *AppointmentService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.23\n contracts\n\n// services/contracts/appointmentServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.\ntype AppointmentServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)\n\n\tFindByStatus(status string) (*AppointmentDTO, error)\n\n\tList(page, limit int) ([]AppointmentDTO, error)\n\n\tUpdate(id uuid.UUID, dto AppointmentDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// AppointmentInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype AppointmentInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tPatientName string `json:\"patient_name\"`\n\tDoctorFullName string `json:\"doctor_full_name\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n}\n\n// AppointmentDTO is the canonical data transfer object passed between layers.\ntype AppointmentDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tPatientID string `json:\"patient_id\"`\n\tDoctorID string `json:\"doctor_id\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n\tStatus string `json:\"status\"`\n}\n\n"} {"category": "service", "text": "\n go1.23\n implementations\n\n// services/implementations/appointmentService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// AppointmentService implements sc.AppointmentServiceContract.\ntype AppointmentService struct {\n\trepo contracts.AppointmentRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewAppointmentService constructs a AppointmentService with all required repositories.\nfunc NewAppointmentService(\n\trepo contracts.AppointmentRepository,\n\torgRepo contracts.OrganizationRepository,\n) *AppointmentService {\n\treturn &AppointmentService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.AppointmentDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {\n\tresult, err := s.repo.FindByStatus(status)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *AppointmentService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.24\n contracts\n\n// services/contracts/appointmentServiceContract.go\n// Pattern: service interface decouples controller from implementation\n// Observed in: Medical-App-Core/services/contracts/\npackage contracts\n\nimport \"github.com/google/uuid\"\n\n// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.\ntype AppointmentServiceContract interface {\n\t// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,\n\t// then delegates persistence to the repository.\n\tCreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)\n\n\tFindByStatus(status string) (*AppointmentDTO, error)\n\n\tList(page, limit int) ([]AppointmentDTO, error)\n\n\tUpdate(id uuid.UUID, dto AppointmentDTO) error\n\n\tDelete(id uuid.UUID) error\n}\n\n// AppointmentInputDTO carries fields exactly as received from the HTTP layer.\n// Name-based fields (OrganizationName, etc.) are resolved in the service.\ntype AppointmentInputDTO struct {\n\tOrganizationName string `json:\"organization_name\"`\n\tPatientName string `json:\"patient_name\"`\n\tDoctorFullName string `json:\"doctor_full_name\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n}\n\n// AppointmentDTO is the canonical data transfer object passed between layers.\ntype AppointmentDTO struct {\n\tOrganizationID string `json:\"organization_id\"`\n\tPatientID string `json:\"patient_id\"`\n\tDoctorID string `json:\"doctor_id\"`\n\tSpecialization string `json:\"specialization\"`\n\tDateTime time.Time `json:\"date_time\"`\n\tStatus string `json:\"status\"`\n}\n\n"} {"category": "service", "text": "\n go1.24\n implementations\n\n// services/implementations/appointmentService.go\n// Pattern: service implementation resolves names→IDs, validates, then calls repo\n// Observed in: Medical-App-Core/services/implementations/\npackage implementations\n\nimport (\n\t\"fmt\"\n\n\t\"medical-sas-api/domain/repositories/contracts\"\n\tsc \"$module/services/contracts\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// AppointmentService implements sc.AppointmentServiceContract.\ntype AppointmentService struct {\n\trepo contracts.AppointmentRepository\n\torgRepo contracts.OrganizationRepository\n}\n\n// NewAppointmentService constructs a AppointmentService with all required repositories.\nfunc NewAppointmentService(\n\trepo contracts.AppointmentRepository,\n\torgRepo contracts.OrganizationRepository,\n) *AppointmentService {\n\treturn &AppointmentService{repo: repo, orgRepo: orgRepo}\n}\n\n// CreateFromInput resolves organization name to UUID in parallel with any other\n// lookups, validates the result, then inserts via the repository.\nfunc (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {\n\tvar (\n\t\torgID uuid.UUID\n\t)\n\n\t// Parallel resolution of related entities\n\tg := &errgroup.Group{}\n\tg.Go(func() error {\n\t\torg, err := s.orgRepo.FindByName(input.OrganizationName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"organization %q not found: %w\", input.OrganizationName, err)\n\t\t}\n\t\torgID = org.ID\n\t\treturn nil\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tdto := sc.AppointmentDTO{\n\t\tOrganizationID: orgID.String(),\n\t\t// map remaining input fields\n\t}\n\treturn s.repo.Create(dto)\n}\n\nfunc (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {\n\tresult, err := s.repo.FindByStatus(status)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"appointment find by status: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {\n\treturn s.repo.List(page, limit)\n}\n\nfunc (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {\n\treturn s.repo.Update(id, dto)\n}\n\nfunc (s *AppointmentService) Delete(id uuid.UUID) error {\n\treturn s.repo.Delete(id)\n}\n\n"} {"category": "service", "text": "\n go1.21\n initializers\n\n// initializers/services.go\n// Pattern: dependency injection container — wires repositories → services\n// Observed in: Medical-App-Core/initializers/services.go\npackage initializers\n\nimport (\n\t\"medical-sas-api/domain/repositories/contracts\"\n\timplrepos \"medical-sas-api/infra/repositories\"\n\tsc \"medical-sas-api/services/contracts\"\n\tsi \"medical-sas-api/services/implementations\"\n\n\t\"gorm.io/gorm\"\n)\n\n// Services is the application-wide DI container.\n// It is constructed once in main() and passed to controllers/middleware.\ntype Services struct {\n\tAuthTokenService sc.AuthTokenServiceContract\n\tOrganizationService sc.OrganizationServiceContract\n\tUserService sc.UserServiceContract\n\tPatientService sc.PatientServiceContract\n\tDoctorService sc.DoctorServiceContract\n\tAppointmentService sc.AppointmentServiceContract\n\tChargeService sc.ChargeServiceContract\n\tMedicalRecordService sc.MedicalRecordServiceContract\n}\n\n// InitServices builds and wires all repositories and services.\nfunc InitServices(db *gorm.DB) *Services {\n\t// Repositories\n\torgRepo := implrepos.NewOrganizationRepository(db)\n\tuserRepo := implrepos.NewUserRepository(db)\n\tpatientRepo := implrepos.NewPatientRepository(db)\n\tdoctorRepo := implrepos.NewDoctorRepository(db)\n\tappointmentRepo := implrepos.NewAppointmentRepository(db)\n\n\t// Services (inject repos as interface values)\n\tauthSvc := si.NewAuthTokenService()\n\torgSvc := si.NewOrganizationService(orgRepo)\n\tuserSvc := si.NewUserService(userRepo, orgRepo)\n\tpatSvc := si.NewPatientService(patientRepo, orgRepo)\n\tdocSvc := si.NewDoctorService(doctorRepo, orgRepo)\n\tapptSvc := si.NewAppointmentService(\n\t\tappointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,\n\t)\n\tchargeSvc := si.NewChargeService(orgRepo)\n\n\treturn &Services{\n\t\tAuthTokenService: authSvc,\n\t\tOrganizationService: orgSvc,\n\t\tUserService: userSvc,\n\t\tPatientService: patSvc,\n\t\tDoctorService: docSvc,\n\t\tAppointmentService: apptSvc,\n\t\tChargeService: chargeSvc,\n\t}\n}\n\n"} {"category": "service", "text": "\n go1.21\n events\n\n// services/events/doctors/doctorEventConsumer.go\n// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling\n// Observed in: Medical-App-Core/services/events/\npackage events\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/streadway/amqp\"\n)\n\n// DoctorCreatedEvent is the message payload published when a doctor is registered.\ntype DoctorCreatedEvent struct {\n\tDoctorID string `json:\"doctor_id\"`\n\tFullName string `json:\"full_name\"`\n\tSpecialty string `json:\"specialty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\n// DoctorEventConsumer subscribes to the doctors.created queue.\ntype DoctorEventConsumer struct {\n\tconn *amqp.Connection\n\tch *amqp.Channel\n\tqueue string\n\thandler func(DoctorCreatedEvent) error\n}\n\n// NewDoctorEventConsumer dials RabbitMQ and declares the queue.\nfunc NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {\n\turl := os.Getenv(\"RABBITMQ_BASE_URL\")\n\tif url == \"\" {\n\t\treturn nil, fmt.Errorf(\"RABBITMQ_BASE_URL is not set\")\n\t}\n\n\tconn, err := amqp.Dial(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rabbitmq dial: %w\", err)\n\t}\n\n\tch, err := conn.Channel()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"rabbitmq channel: %w\", err)\n\t}\n\n\tqueue := \"doctors.created\"\n\t_, err = ch.QueueDeclare(queue, true, false, false, false, nil)\n\tif err != nil {\n\t\tch.Close(); conn.Close()\n\t\treturn nil, fmt.Errorf(\"queue declare: %w\", err)\n\t}\n\n\treturn &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil\n}\n\n// Consume starts consuming messages and blocks until ctx is cancelled.\nfunc (c *DoctorEventConsumer) Consume() error {\n\tmsgs, err := c.ch.Consume(c.queue, \"\", false, false, false, false, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"consume: %w\", err)\n\t}\n\n\tlog.Printf(\"consuming from %s\", c.queue)\n\tfor msg := range msgs {\n\t\tvar event DoctorCreatedEvent\n\t\tif err := json.Unmarshal(msg.Body, &event); err != nil {\n\t\t\tlog.Printf(\"unmarshal error: %v — nacking\", err)\n\t\t\tmsg.Nack(false, false) // dead-letter\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.handler(event); err != nil {\n\t\t\tlog.Printf(\"handler error: %v — nacking with requeue\", err)\n\t\t\tmsg.Nack(false, true)\n\t\t\tcontinue\n\t\t}\n\t\tmsg.Ack(false)\n\t}\n\treturn nil\n}\n\n// Close releases RabbitMQ resources.\nfunc (c *DoctorEventConsumer) Close() {\n\tc.ch.Close()\n\tc.conn.Close()\n}\n\n"} {"category": "service", "text": "\n go1.22\n initializers\n\n// initializers/services.go\n// Pattern: dependency injection container — wires repositories → services\n// Observed in: Medical-App-Core/initializers/services.go\npackage initializers\n\nimport (\n\t\"medical-sas-api/domain/repositories/contracts\"\n\timplrepos \"medical-sas-api/infra/repositories\"\n\tsc \"medical-sas-api/services/contracts\"\n\tsi \"medical-sas-api/services/implementations\"\n\n\t\"gorm.io/gorm\"\n)\n\n// Services is the application-wide DI container.\n// It is constructed once in main() and passed to controllers/middleware.\ntype Services struct {\n\tAuthTokenService sc.AuthTokenServiceContract\n\tOrganizationService sc.OrganizationServiceContract\n\tUserService sc.UserServiceContract\n\tPatientService sc.PatientServiceContract\n\tDoctorService sc.DoctorServiceContract\n\tAppointmentService sc.AppointmentServiceContract\n\tChargeService sc.ChargeServiceContract\n\tMedicalRecordService sc.MedicalRecordServiceContract\n}\n\n// InitServices builds and wires all repositories and services.\nfunc InitServices(db *gorm.DB) *Services {\n\t// Repositories\n\torgRepo := implrepos.NewOrganizationRepository(db)\n\tuserRepo := implrepos.NewUserRepository(db)\n\tpatientRepo := implrepos.NewPatientRepository(db)\n\tdoctorRepo := implrepos.NewDoctorRepository(db)\n\tappointmentRepo := implrepos.NewAppointmentRepository(db)\n\n\t// Services (inject repos as interface values)\n\tauthSvc := si.NewAuthTokenService()\n\torgSvc := si.NewOrganizationService(orgRepo)\n\tuserSvc := si.NewUserService(userRepo, orgRepo)\n\tpatSvc := si.NewPatientService(patientRepo, orgRepo)\n\tdocSvc := si.NewDoctorService(doctorRepo, orgRepo)\n\tapptSvc := si.NewAppointmentService(\n\t\tappointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,\n\t)\n\tchargeSvc := si.NewChargeService(orgRepo)\n\n\treturn &Services{\n\t\tAuthTokenService: authSvc,\n\t\tOrganizationService: orgSvc,\n\t\tUserService: userSvc,\n\t\tPatientService: patSvc,\n\t\tDoctorService: docSvc,\n\t\tAppointmentService: apptSvc,\n\t\tChargeService: chargeSvc,\n\t}\n}\n\n"} {"category": "service", "text": "\n go1.22\n events\n\n// services/events/doctors/doctorEventConsumer.go\n// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling\n// Observed in: Medical-App-Core/services/events/\npackage events\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/streadway/amqp\"\n)\n\n// DoctorCreatedEvent is the message payload published when a doctor is registered.\ntype DoctorCreatedEvent struct {\n\tDoctorID string `json:\"doctor_id\"`\n\tFullName string `json:\"full_name\"`\n\tSpecialty string `json:\"specialty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\n// DoctorEventConsumer subscribes to the doctors.created queue.\ntype DoctorEventConsumer struct {\n\tconn *amqp.Connection\n\tch *amqp.Channel\n\tqueue string\n\thandler func(DoctorCreatedEvent) error\n}\n\n// NewDoctorEventConsumer dials RabbitMQ and declares the queue.\nfunc NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {\n\turl := os.Getenv(\"RABBITMQ_BASE_URL\")\n\tif url == \"\" {\n\t\treturn nil, fmt.Errorf(\"RABBITMQ_BASE_URL is not set\")\n\t}\n\n\tconn, err := amqp.Dial(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rabbitmq dial: %w\", err)\n\t}\n\n\tch, err := conn.Channel()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"rabbitmq channel: %w\", err)\n\t}\n\n\tqueue := \"doctors.created\"\n\t_, err = ch.QueueDeclare(queue, true, false, false, false, nil)\n\tif err != nil {\n\t\tch.Close(); conn.Close()\n\t\treturn nil, fmt.Errorf(\"queue declare: %w\", err)\n\t}\n\n\treturn &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil\n}\n\n// Consume starts consuming messages and blocks until ctx is cancelled.\nfunc (c *DoctorEventConsumer) Consume() error {\n\tmsgs, err := c.ch.Consume(c.queue, \"\", false, false, false, false, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"consume: %w\", err)\n\t}\n\n\tlog.Printf(\"consuming from %s\", c.queue)\n\tfor msg := range msgs {\n\t\tvar event DoctorCreatedEvent\n\t\tif err := json.Unmarshal(msg.Body, &event); err != nil {\n\t\t\tlog.Printf(\"unmarshal error: %v — nacking\", err)\n\t\t\tmsg.Nack(false, false) // dead-letter\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.handler(event); err != nil {\n\t\t\tlog.Printf(\"handler error: %v — nacking with requeue\", err)\n\t\t\tmsg.Nack(false, true)\n\t\t\tcontinue\n\t\t}\n\t\tmsg.Ack(false)\n\t}\n\treturn nil\n}\n\n// Close releases RabbitMQ resources.\nfunc (c *DoctorEventConsumer) Close() {\n\tc.ch.Close()\n\tc.conn.Close()\n}\n\n"} {"category": "service", "text": "\n go1.23\n initializers\n\n// initializers/services.go\n// Pattern: dependency injection container — wires repositories → services\n// Observed in: Medical-App-Core/initializers/services.go\npackage initializers\n\nimport (\n\t\"medical-sas-api/domain/repositories/contracts\"\n\timplrepos \"medical-sas-api/infra/repositories\"\n\tsc \"medical-sas-api/services/contracts\"\n\tsi \"medical-sas-api/services/implementations\"\n\n\t\"gorm.io/gorm\"\n)\n\n// Services is the application-wide DI container.\n// It is constructed once in main() and passed to controllers/middleware.\ntype Services struct {\n\tAuthTokenService sc.AuthTokenServiceContract\n\tOrganizationService sc.OrganizationServiceContract\n\tUserService sc.UserServiceContract\n\tPatientService sc.PatientServiceContract\n\tDoctorService sc.DoctorServiceContract\n\tAppointmentService sc.AppointmentServiceContract\n\tChargeService sc.ChargeServiceContract\n\tMedicalRecordService sc.MedicalRecordServiceContract\n}\n\n// InitServices builds and wires all repositories and services.\nfunc InitServices(db *gorm.DB) *Services {\n\t// Repositories\n\torgRepo := implrepos.NewOrganizationRepository(db)\n\tuserRepo := implrepos.NewUserRepository(db)\n\tpatientRepo := implrepos.NewPatientRepository(db)\n\tdoctorRepo := implrepos.NewDoctorRepository(db)\n\tappointmentRepo := implrepos.NewAppointmentRepository(db)\n\n\t// Services (inject repos as interface values)\n\tauthSvc := si.NewAuthTokenService()\n\torgSvc := si.NewOrganizationService(orgRepo)\n\tuserSvc := si.NewUserService(userRepo, orgRepo)\n\tpatSvc := si.NewPatientService(patientRepo, orgRepo)\n\tdocSvc := si.NewDoctorService(doctorRepo, orgRepo)\n\tapptSvc := si.NewAppointmentService(\n\t\tappointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,\n\t)\n\tchargeSvc := si.NewChargeService(orgRepo)\n\n\treturn &Services{\n\t\tAuthTokenService: authSvc,\n\t\tOrganizationService: orgSvc,\n\t\tUserService: userSvc,\n\t\tPatientService: patSvc,\n\t\tDoctorService: docSvc,\n\t\tAppointmentService: apptSvc,\n\t\tChargeService: chargeSvc,\n\t}\n}\n\n"} {"category": "service", "text": "\n go1.23\n events\n\n// services/events/doctors/doctorEventConsumer.go\n// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling\n// Observed in: Medical-App-Core/services/events/\npackage events\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/streadway/amqp\"\n)\n\n// DoctorCreatedEvent is the message payload published when a doctor is registered.\ntype DoctorCreatedEvent struct {\n\tDoctorID string `json:\"doctor_id\"`\n\tFullName string `json:\"full_name\"`\n\tSpecialty string `json:\"specialty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\n// DoctorEventConsumer subscribes to the doctors.created queue.\ntype DoctorEventConsumer struct {\n\tconn *amqp.Connection\n\tch *amqp.Channel\n\tqueue string\n\thandler func(DoctorCreatedEvent) error\n}\n\n// NewDoctorEventConsumer dials RabbitMQ and declares the queue.\nfunc NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {\n\turl := os.Getenv(\"RABBITMQ_BASE_URL\")\n\tif url == \"\" {\n\t\treturn nil, fmt.Errorf(\"RABBITMQ_BASE_URL is not set\")\n\t}\n\n\tconn, err := amqp.Dial(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rabbitmq dial: %w\", err)\n\t}\n\n\tch, err := conn.Channel()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"rabbitmq channel: %w\", err)\n\t}\n\n\tqueue := \"doctors.created\"\n\t_, err = ch.QueueDeclare(queue, true, false, false, false, nil)\n\tif err != nil {\n\t\tch.Close(); conn.Close()\n\t\treturn nil, fmt.Errorf(\"queue declare: %w\", err)\n\t}\n\n\treturn &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil\n}\n\n// Consume starts consuming messages and blocks until ctx is cancelled.\nfunc (c *DoctorEventConsumer) Consume() error {\n\tmsgs, err := c.ch.Consume(c.queue, \"\", false, false, false, false, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"consume: %w\", err)\n\t}\n\n\tlog.Printf(\"consuming from %s\", c.queue)\n\tfor msg := range msgs {\n\t\tvar event DoctorCreatedEvent\n\t\tif err := json.Unmarshal(msg.Body, &event); err != nil {\n\t\t\tlog.Printf(\"unmarshal error: %v — nacking\", err)\n\t\t\tmsg.Nack(false, false) // dead-letter\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.handler(event); err != nil {\n\t\t\tlog.Printf(\"handler error: %v — nacking with requeue\", err)\n\t\t\tmsg.Nack(false, true)\n\t\t\tcontinue\n\t\t}\n\t\tmsg.Ack(false)\n\t}\n\treturn nil\n}\n\n// Close releases RabbitMQ resources.\nfunc (c *DoctorEventConsumer) Close() {\n\tc.ch.Close()\n\tc.conn.Close()\n}\n\n"} {"category": "service", "text": "\n go1.24\n initializers\n\n// initializers/services.go\n// Pattern: dependency injection container — wires repositories → services\n// Observed in: Medical-App-Core/initializers/services.go\npackage initializers\n\nimport (\n\t\"medical-sas-api/domain/repositories/contracts\"\n\timplrepos \"medical-sas-api/infra/repositories\"\n\tsc \"medical-sas-api/services/contracts\"\n\tsi \"medical-sas-api/services/implementations\"\n\n\t\"gorm.io/gorm\"\n)\n\n// Services is the application-wide DI container.\n// It is constructed once in main() and passed to controllers/middleware.\ntype Services struct {\n\tAuthTokenService sc.AuthTokenServiceContract\n\tOrganizationService sc.OrganizationServiceContract\n\tUserService sc.UserServiceContract\n\tPatientService sc.PatientServiceContract\n\tDoctorService sc.DoctorServiceContract\n\tAppointmentService sc.AppointmentServiceContract\n\tChargeService sc.ChargeServiceContract\n\tMedicalRecordService sc.MedicalRecordServiceContract\n}\n\n// InitServices builds and wires all repositories and services.\nfunc InitServices(db *gorm.DB) *Services {\n\t// Repositories\n\torgRepo := implrepos.NewOrganizationRepository(db)\n\tuserRepo := implrepos.NewUserRepository(db)\n\tpatientRepo := implrepos.NewPatientRepository(db)\n\tdoctorRepo := implrepos.NewDoctorRepository(db)\n\tappointmentRepo := implrepos.NewAppointmentRepository(db)\n\n\t// Services (inject repos as interface values)\n\tauthSvc := si.NewAuthTokenService()\n\torgSvc := si.NewOrganizationService(orgRepo)\n\tuserSvc := si.NewUserService(userRepo, orgRepo)\n\tpatSvc := si.NewPatientService(patientRepo, orgRepo)\n\tdocSvc := si.NewDoctorService(doctorRepo, orgRepo)\n\tapptSvc := si.NewAppointmentService(\n\t\tappointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,\n\t)\n\tchargeSvc := si.NewChargeService(orgRepo)\n\n\treturn &Services{\n\t\tAuthTokenService: authSvc,\n\t\tOrganizationService: orgSvc,\n\t\tUserService: userSvc,\n\t\tPatientService: patSvc,\n\t\tDoctorService: docSvc,\n\t\tAppointmentService: apptSvc,\n\t\tChargeService: chargeSvc,\n\t}\n}\n\n"} {"category": "service", "text": "\n go1.24\n events\n\n// services/events/doctors/doctorEventConsumer.go\n// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling\n// Observed in: Medical-App-Core/services/events/\npackage events\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/streadway/amqp\"\n)\n\n// DoctorCreatedEvent is the message payload published when a doctor is registered.\ntype DoctorCreatedEvent struct {\n\tDoctorID string `json:\"doctor_id\"`\n\tFullName string `json:\"full_name\"`\n\tSpecialty string `json:\"specialty\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\n// DoctorEventConsumer subscribes to the doctors.created queue.\ntype DoctorEventConsumer struct {\n\tconn *amqp.Connection\n\tch *amqp.Channel\n\tqueue string\n\thandler func(DoctorCreatedEvent) error\n}\n\n// NewDoctorEventConsumer dials RabbitMQ and declares the queue.\nfunc NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {\n\turl := os.Getenv(\"RABBITMQ_BASE_URL\")\n\tif url == \"\" {\n\t\treturn nil, fmt.Errorf(\"RABBITMQ_BASE_URL is not set\")\n\t}\n\n\tconn, err := amqp.Dial(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rabbitmq dial: %w\", err)\n\t}\n\n\tch, err := conn.Channel()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"rabbitmq channel: %w\", err)\n\t}\n\n\tqueue := \"doctors.created\"\n\t_, err = ch.QueueDeclare(queue, true, false, false, false, nil)\n\tif err != nil {\n\t\tch.Close(); conn.Close()\n\t\treturn nil, fmt.Errorf(\"queue declare: %w\", err)\n\t}\n\n\treturn &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil\n}\n\n// Consume starts consuming messages and blocks until ctx is cancelled.\nfunc (c *DoctorEventConsumer) Consume() error {\n\tmsgs, err := c.ch.Consume(c.queue, \"\", false, false, false, false, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"consume: %w\", err)\n\t}\n\n\tlog.Printf(\"consuming from %s\", c.queue)\n\tfor msg := range msgs {\n\t\tvar event DoctorCreatedEvent\n\t\tif err := json.Unmarshal(msg.Body, &event); err != nil {\n\t\t\tlog.Printf(\"unmarshal error: %v — nacking\", err)\n\t\t\tmsg.Nack(false, false) // dead-letter\n\t\t\tcontinue\n\t\t}\n\t\tif err := c.handler(event); err != nil {\n\t\t\tlog.Printf(\"handler error: %v — nacking with requeue\", err)\n\t\t\tmsg.Nack(false, true)\n\t\t\tcontinue\n\t\t}\n\t\tmsg.Ack(false)\n\t}\n\treturn nil\n}\n\n// Close releases RabbitMQ resources.\nfunc (c *DoctorEventConsumer) Close() {\n\tc.ch.Close()\n\tc.conn.Close()\n}\n\n"} {"category": "auth", "text": "\n go1.21\n implementations\n\n// services/implementations/auth_service.go\n// Pattern: JWT HS256 token generation + validation service\n// Observed in: Medical-App-Core/services/implementations/auth_service.go\npackage implementations\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// Claims holds the JWT payload.\ntype Claims struct {\n\tUserID string `json:\"user_id\"`\n\tEmail string `json:\"email\"`\n\tRole string `json:\"role\"`\n\tjwt.RegisteredClaims\n}\n\n// AuthTokenServiceImpl handles JWT creation and validation.\ntype AuthTokenServiceImpl struct {\n\tsecret []byte\n\tttl time.Duration\n}\n\n// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.\nfunc NewAuthTokenService() *AuthTokenServiceImpl {\n\tsecret := os.Getenv(\"JWT_SECRET\")\n\tif secret == \"\" {\n\t\tpanic(\"JWT_SECRET environment variable must be set\")\n\t}\n\treturn &AuthTokenServiceImpl{\n\t\tsecret: []byte(secret),\n\t\tttl: 24 * time.Hour,\n\t}\n}\n\n// GenerateToken creates a signed HS256 JWT for the given user attributes.\nfunc (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {\n\tclaims := &Claims{\n\t\tUserID: userID,\n\t\tEmail: email,\n\t\tRole: role,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),\n\t\t\tIssuedAt: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer: \"medical-sas-api\",\n\t\t},\n\t}\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tsigned, err := token.SignedString(s.secret)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"sign token: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// ValidateToken parses and validates a JWT string. Returns nil on success.\nfunc (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {\n\t\tif _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", t.Header[\"alg\"])\n\t\t}\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid token: %w\", err)\n\t}\n\tif !token.Valid {\n\t\treturn fmt.Errorf(\"token is not valid\")\n\t}\n\treturn nil\n}\n\n// ParseClaims extracts the Claims from a valid JWT string.\nfunc (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {\n\tclaims := &Claims{}\n\t_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse claims: %w\", err)\n\t}\n\treturn claims, nil\n}\n\n// HashPassword returns a bcrypt hash of the plain-text password.\nfunc HashPassword(plain string) (string, error) {\n\thashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"hash password: %w\", err)\n\t}\n\treturn string(hashed), nil\n}\n\n// CheckPassword returns nil if plain matches the bcrypt hash.\nfunc CheckPassword(plain, hash string) error {\n\tif err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {\n\t\treturn fmt.Errorf(\"invalid credentials\")\n\t}\n\treturn nil\n}\n\n"} {"category": "auth", "text": "\n go1.21\n middleware\n\n// middleware/auth.go\n// Pattern: Fiber JWT bearer middleware with claims propagation via Locals\n// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware\npackage middleware\n\nimport (\n\t\"strings\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// BearerAuth validates the Authorization: Bearer header and stores parsed\n// claims in fiber.Ctx.Locals(\"claims\") for downstream handlers.\nfunc BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif !strings.HasPrefix(header, \"Bearer \") {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\n\t\ttokenStr := strings.TrimPrefix(header, \"Bearer \")\n\t\tclaims, err := authSvc.ParseClaims(tokenStr)\n\t\tif err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\n\t\t// Make claims available to all downstream handlers\n\t\tc.Locals(\"claims\", claims)\n\t\tc.Locals(\"user_id\", claims.UserID)\n\t\tc.Locals(\"role\", claims.Role)\n\t\treturn c.Next()\n\t}\n}\n\n// RoleRequired returns a middleware that allows only the specified roles.\n// Must be chained after BearerAuth.\nfunc RoleRequired(roles ...string) fiber.Handler {\n\tallowed := make(map[string]struct{}, len(roles))\n\tfor _, r := range roles {\n\t\tallowed[r] = struct{}{}\n\t}\n\treturn func(c *fiber.Ctx) error {\n\t\trole, _ := c.Locals(\"role\").(string)\n\t\tif _, ok := allowed[role]; !ok {\n\t\t\treturn c.Status(fiber.StatusForbidden).JSON(fiber.Map{\n\t\t\t\t\"error\": \"insufficient permissions\",\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\n"} {"category": "auth", "text": "\n go1.21\n controllers\n\n// controllers/auth_controller.go\n// Pattern: login + token generation handler\n// Observed in: Medical-App-Core/controllers/auth_controller.go\npackage controllers\n\nimport (\n\t\"net/http\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// AuthController handles token generation and validation endpoints.\ntype AuthController struct {\n\tauthSvc *implementations.AuthTokenServiceImpl\n}\n\nfunc NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {\n\treturn &AuthController{authSvc: svc}\n}\n\ntype generateTokenRequest struct {\n\tUserID string `json:\"user_id\" validate:\"required,uuid4\"`\n\tEmail string `json:\"email\" validate:\"required,email\"`\n\tRole string `json:\"role\" validate:\"required\"`\n}\n\n// GenerateToken godoc\n// @Summary Issue a JWT token\n// @Description Generates a signed JWT for the given user credentials\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body generateTokenRequest true \"Token request\"\n// @Success 200 {object} fiber.Map{token=string}\n// @Failure 400 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /auth/token [post]\nfunc (ac *AuthController) GenerateToken(c *fiber.Ctx) error {\n\tvar req generateTokenRequest\n\tif err := c.BodyParser(&req); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\tif req.UserID == \"\" || req.Email == \"\" || req.Role == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"user_id, email, and role are required\"})\n\t}\n\n\ttoken, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to generate token\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"token\": token})\n}\n\n// ValidateToken godoc\n// @Summary Validate a JWT token\n// @Description Returns 200 if the token is valid, 401 otherwise\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body fiber.Map{token=string} true \"Token to validate\"\n// @Success 200 {object} fiber.Map{valid=bool}\n// @Failure 401 {object} fiber.Map\n// @Router /auth/validate [post]\nfunc (ac *AuthController) ValidateToken(c *fiber.Ctx) error {\n\tvar body struct {\n\t\tToken string `json:\"token\"`\n\t}\n\tif err := c.BodyParser(&body); err != nil || body.Token == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"token is required\"})\n\t}\n\n\tif err := ac.authSvc.ValidateToken(body.Token); err != nil {\n\t\treturn c.Status(http.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\"valid\": false,\n\t\t\t\"reason\": err.Error(),\n\t\t})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"valid\": true})\n}\n\n"} {"category": "auth", "text": "\n go1.21\n initializers\n\n// initializers/validators.go\n// Pattern: custom validator registration with go-playground/validator\n// Observed in: Medical-App-Core/initializers/validators.go\npackage initializers\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\n// RegisterCustomValidators adds domain-specific validation rules.\nfunc RegisterCustomValidators(v *validator.Validate) {\n\t_ = v.RegisterValidation(\"cpf\", validateCPF)\n\t_ = v.RegisterValidation(\"cnpj\", validateCNPJ)\n\t_ = v.RegisterValidation(\"crm\", validateCRM)\n}\n\n// validateCPF validates a Brazilian CPF number (11 digits, checksum).\nfunc validateCPF(fl validator.FieldLevel) bool {\n\tcpf := strings.ReplaceAll(fl.Field().String(), \".\", \"\")\n\tcpf = strings.ReplaceAll(cpf, \"-\", \"\")\n\tif len(cpf) != 11 || allEqual(cpf) {\n\t\treturn false\n\t}\n\treturn cpfChecksum(cpf)\n}\n\nfunc cpfChecksum(cpf string) bool {\n\tdigits := make([]int, 11)\n\tfor i, ch := range cpf {\n\t\tdigits[i], _ = strconv.Atoi(string(ch))\n\t}\n\tfor pass := 0; pass < 2; pass++ {\n\t\tweight := 10 + pass\n\t\tsum := 0\n\t\tfor i := 0; i < 9+pass; i++ {\n\t\t\tsum += digits[i] * (weight - i)\n\t\t}\n\t\trem := (sum * 10) % 11\n\t\tif rem == 10 || rem == 11 {\n\t\t\trem = 0\n\t\t}\n\t\tif rem != digits[9+pass] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).\nfunc validateCNPJ(fl validator.FieldLevel) bool {\n\tcnpj := strings.Map(func(r rune) rune {\n\t\tif r >= '0' && r <= '9' { return r }\n\t\treturn -1\n\t}, fl.Field().String())\n\tif len(cnpj) != 14 || allEqual(cnpj) {\n\t\treturn false\n\t}\n\treturn cnpjChecksum(cnpj)\n}\n\nfunc cnpjChecksum(cnpj string) bool {\n\tweights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tweights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tfor pass, weights := range [][]int{weights1, weights2} {\n\t\tsum := 0\n\t\tfor i, w := range weights {\n\t\t\td, _ := strconv.Atoi(string(cnpj[i]))\n\t\t\tsum += d * w\n\t\t}\n\t\trem := sum % 11\n\t\texpected := byte('0')\n\t\tif rem >= 2 {\n\t\t\texpected = byte('0' + (11 - rem))\n\t\t}\n\t\tif cnpj[12+pass] != expected {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).\nfunc validateCRM(fl validator.FieldLevel) bool {\n\tcrm := fl.Field().String()\n\treturn len(crm) >= 4 && len(crm) <= 10\n}\n\nfunc allEqual(s string) bool {\n\tif s == \"\" { return false }\n\tfor _, c := range s[1:] {\n\t\tif byte(c) != s[0] { return false }\n\t}\n\treturn true\n}\n\n"} {"category": "auth", "text": "\n go1.22\n implementations\n\n// services/implementations/auth_service.go\n// Pattern: JWT HS256 token generation + validation service\n// Observed in: Medical-App-Core/services/implementations/auth_service.go\npackage implementations\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// Claims holds the JWT payload.\ntype Claims struct {\n\tUserID string `json:\"user_id\"`\n\tEmail string `json:\"email\"`\n\tRole string `json:\"role\"`\n\tjwt.RegisteredClaims\n}\n\n// AuthTokenServiceImpl handles JWT creation and validation.\ntype AuthTokenServiceImpl struct {\n\tsecret []byte\n\tttl time.Duration\n}\n\n// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.\nfunc NewAuthTokenService() *AuthTokenServiceImpl {\n\tsecret := os.Getenv(\"JWT_SECRET\")\n\tif secret == \"\" {\n\t\tpanic(\"JWT_SECRET environment variable must be set\")\n\t}\n\treturn &AuthTokenServiceImpl{\n\t\tsecret: []byte(secret),\n\t\tttl: 24 * time.Hour,\n\t}\n}\n\n// GenerateToken creates a signed HS256 JWT for the given user attributes.\nfunc (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {\n\tclaims := &Claims{\n\t\tUserID: userID,\n\t\tEmail: email,\n\t\tRole: role,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),\n\t\t\tIssuedAt: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer: \"medical-sas-api\",\n\t\t},\n\t}\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tsigned, err := token.SignedString(s.secret)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"sign token: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// ValidateToken parses and validates a JWT string. Returns nil on success.\nfunc (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {\n\t\tif _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", t.Header[\"alg\"])\n\t\t}\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid token: %w\", err)\n\t}\n\tif !token.Valid {\n\t\treturn fmt.Errorf(\"token is not valid\")\n\t}\n\treturn nil\n}\n\n// ParseClaims extracts the Claims from a valid JWT string.\nfunc (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {\n\tclaims := &Claims{}\n\t_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse claims: %w\", err)\n\t}\n\treturn claims, nil\n}\n\n// HashPassword returns a bcrypt hash of the plain-text password.\nfunc HashPassword(plain string) (string, error) {\n\thashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"hash password: %w\", err)\n\t}\n\treturn string(hashed), nil\n}\n\n// CheckPassword returns nil if plain matches the bcrypt hash.\nfunc CheckPassword(plain, hash string) error {\n\tif err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {\n\t\treturn fmt.Errorf(\"invalid credentials\")\n\t}\n\treturn nil\n}\n\n"} {"category": "auth", "text": "\n go1.22\n middleware\n\n// middleware/auth.go\n// Pattern: Fiber JWT bearer middleware with claims propagation via Locals\n// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware\npackage middleware\n\nimport (\n\t\"strings\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// BearerAuth validates the Authorization: Bearer header and stores parsed\n// claims in fiber.Ctx.Locals(\"claims\") for downstream handlers.\nfunc BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif !strings.HasPrefix(header, \"Bearer \") {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\n\t\ttokenStr := strings.TrimPrefix(header, \"Bearer \")\n\t\tclaims, err := authSvc.ParseClaims(tokenStr)\n\t\tif err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\n\t\t// Make claims available to all downstream handlers\n\t\tc.Locals(\"claims\", claims)\n\t\tc.Locals(\"user_id\", claims.UserID)\n\t\tc.Locals(\"role\", claims.Role)\n\t\treturn c.Next()\n\t}\n}\n\n// RoleRequired returns a middleware that allows only the specified roles.\n// Must be chained after BearerAuth.\nfunc RoleRequired(roles ...string) fiber.Handler {\n\tallowed := make(map[string]struct{}, len(roles))\n\tfor _, r := range roles {\n\t\tallowed[r] = struct{}{}\n\t}\n\treturn func(c *fiber.Ctx) error {\n\t\trole, _ := c.Locals(\"role\").(string)\n\t\tif _, ok := allowed[role]; !ok {\n\t\t\treturn c.Status(fiber.StatusForbidden).JSON(fiber.Map{\n\t\t\t\t\"error\": \"insufficient permissions\",\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\n"} {"category": "auth", "text": "\n go1.22\n controllers\n\n// controllers/auth_controller.go\n// Pattern: login + token generation handler\n// Observed in: Medical-App-Core/controllers/auth_controller.go\npackage controllers\n\nimport (\n\t\"net/http\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// AuthController handles token generation and validation endpoints.\ntype AuthController struct {\n\tauthSvc *implementations.AuthTokenServiceImpl\n}\n\nfunc NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {\n\treturn &AuthController{authSvc: svc}\n}\n\ntype generateTokenRequest struct {\n\tUserID string `json:\"user_id\" validate:\"required,uuid4\"`\n\tEmail string `json:\"email\" validate:\"required,email\"`\n\tRole string `json:\"role\" validate:\"required\"`\n}\n\n// GenerateToken godoc\n// @Summary Issue a JWT token\n// @Description Generates a signed JWT for the given user credentials\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body generateTokenRequest true \"Token request\"\n// @Success 200 {object} fiber.Map{token=string}\n// @Failure 400 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /auth/token [post]\nfunc (ac *AuthController) GenerateToken(c *fiber.Ctx) error {\n\tvar req generateTokenRequest\n\tif err := c.BodyParser(&req); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\tif req.UserID == \"\" || req.Email == \"\" || req.Role == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"user_id, email, and role are required\"})\n\t}\n\n\ttoken, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to generate token\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"token\": token})\n}\n\n// ValidateToken godoc\n// @Summary Validate a JWT token\n// @Description Returns 200 if the token is valid, 401 otherwise\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body fiber.Map{token=string} true \"Token to validate\"\n// @Success 200 {object} fiber.Map{valid=bool}\n// @Failure 401 {object} fiber.Map\n// @Router /auth/validate [post]\nfunc (ac *AuthController) ValidateToken(c *fiber.Ctx) error {\n\tvar body struct {\n\t\tToken string `json:\"token\"`\n\t}\n\tif err := c.BodyParser(&body); err != nil || body.Token == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"token is required\"})\n\t}\n\n\tif err := ac.authSvc.ValidateToken(body.Token); err != nil {\n\t\treturn c.Status(http.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\"valid\": false,\n\t\t\t\"reason\": err.Error(),\n\t\t})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"valid\": true})\n}\n\n"} {"category": "auth", "text": "\n go1.22\n initializers\n\n// initializers/validators.go\n// Pattern: custom validator registration with go-playground/validator\n// Observed in: Medical-App-Core/initializers/validators.go\npackage initializers\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\n// RegisterCustomValidators adds domain-specific validation rules.\nfunc RegisterCustomValidators(v *validator.Validate) {\n\t_ = v.RegisterValidation(\"cpf\", validateCPF)\n\t_ = v.RegisterValidation(\"cnpj\", validateCNPJ)\n\t_ = v.RegisterValidation(\"crm\", validateCRM)\n}\n\n// validateCPF validates a Brazilian CPF number (11 digits, checksum).\nfunc validateCPF(fl validator.FieldLevel) bool {\n\tcpf := strings.ReplaceAll(fl.Field().String(), \".\", \"\")\n\tcpf = strings.ReplaceAll(cpf, \"-\", \"\")\n\tif len(cpf) != 11 || allEqual(cpf) {\n\t\treturn false\n\t}\n\treturn cpfChecksum(cpf)\n}\n\nfunc cpfChecksum(cpf string) bool {\n\tdigits := make([]int, 11)\n\tfor i, ch := range cpf {\n\t\tdigits[i], _ = strconv.Atoi(string(ch))\n\t}\n\tfor pass := 0; pass < 2; pass++ {\n\t\tweight := 10 + pass\n\t\tsum := 0\n\t\tfor i := 0; i < 9+pass; i++ {\n\t\t\tsum += digits[i] * (weight - i)\n\t\t}\n\t\trem := (sum * 10) % 11\n\t\tif rem == 10 || rem == 11 {\n\t\t\trem = 0\n\t\t}\n\t\tif rem != digits[9+pass] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).\nfunc validateCNPJ(fl validator.FieldLevel) bool {\n\tcnpj := strings.Map(func(r rune) rune {\n\t\tif r >= '0' && r <= '9' { return r }\n\t\treturn -1\n\t}, fl.Field().String())\n\tif len(cnpj) != 14 || allEqual(cnpj) {\n\t\treturn false\n\t}\n\treturn cnpjChecksum(cnpj)\n}\n\nfunc cnpjChecksum(cnpj string) bool {\n\tweights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tweights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tfor pass, weights := range [][]int{weights1, weights2} {\n\t\tsum := 0\n\t\tfor i, w := range weights {\n\t\t\td, _ := strconv.Atoi(string(cnpj[i]))\n\t\t\tsum += d * w\n\t\t}\n\t\trem := sum % 11\n\t\texpected := byte('0')\n\t\tif rem >= 2 {\n\t\t\texpected = byte('0' + (11 - rem))\n\t\t}\n\t\tif cnpj[12+pass] != expected {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).\nfunc validateCRM(fl validator.FieldLevel) bool {\n\tcrm := fl.Field().String()\n\treturn len(crm) >= 4 && len(crm) <= 10\n}\n\nfunc allEqual(s string) bool {\n\tif s == \"\" { return false }\n\tfor _, c := range s[1:] {\n\t\tif byte(c) != s[0] { return false }\n\t}\n\treturn true\n}\n\n"} {"category": "auth", "text": "\n go1.23\n implementations\n\n// services/implementations/auth_service.go\n// Pattern: JWT HS256 token generation + validation service\n// Observed in: Medical-App-Core/services/implementations/auth_service.go\npackage implementations\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// Claims holds the JWT payload.\ntype Claims struct {\n\tUserID string `json:\"user_id\"`\n\tEmail string `json:\"email\"`\n\tRole string `json:\"role\"`\n\tjwt.RegisteredClaims\n}\n\n// AuthTokenServiceImpl handles JWT creation and validation.\ntype AuthTokenServiceImpl struct {\n\tsecret []byte\n\tttl time.Duration\n}\n\n// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.\nfunc NewAuthTokenService() *AuthTokenServiceImpl {\n\tsecret := os.Getenv(\"JWT_SECRET\")\n\tif secret == \"\" {\n\t\tpanic(\"JWT_SECRET environment variable must be set\")\n\t}\n\treturn &AuthTokenServiceImpl{\n\t\tsecret: []byte(secret),\n\t\tttl: 24 * time.Hour,\n\t}\n}\n\n// GenerateToken creates a signed HS256 JWT for the given user attributes.\nfunc (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {\n\tclaims := &Claims{\n\t\tUserID: userID,\n\t\tEmail: email,\n\t\tRole: role,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),\n\t\t\tIssuedAt: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer: \"medical-sas-api\",\n\t\t},\n\t}\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tsigned, err := token.SignedString(s.secret)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"sign token: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// ValidateToken parses and validates a JWT string. Returns nil on success.\nfunc (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {\n\t\tif _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", t.Header[\"alg\"])\n\t\t}\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid token: %w\", err)\n\t}\n\tif !token.Valid {\n\t\treturn fmt.Errorf(\"token is not valid\")\n\t}\n\treturn nil\n}\n\n// ParseClaims extracts the Claims from a valid JWT string.\nfunc (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {\n\tclaims := &Claims{}\n\t_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse claims: %w\", err)\n\t}\n\treturn claims, nil\n}\n\n// HashPassword returns a bcrypt hash of the plain-text password.\nfunc HashPassword(plain string) (string, error) {\n\thashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"hash password: %w\", err)\n\t}\n\treturn string(hashed), nil\n}\n\n// CheckPassword returns nil if plain matches the bcrypt hash.\nfunc CheckPassword(plain, hash string) error {\n\tif err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {\n\t\treturn fmt.Errorf(\"invalid credentials\")\n\t}\n\treturn nil\n}\n\n"} {"category": "auth", "text": "\n go1.23\n middleware\n\n// middleware/auth.go\n// Pattern: Fiber JWT bearer middleware with claims propagation via Locals\n// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware\npackage middleware\n\nimport (\n\t\"strings\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// BearerAuth validates the Authorization: Bearer header and stores parsed\n// claims in fiber.Ctx.Locals(\"claims\") for downstream handlers.\nfunc BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif !strings.HasPrefix(header, \"Bearer \") {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\n\t\ttokenStr := strings.TrimPrefix(header, \"Bearer \")\n\t\tclaims, err := authSvc.ParseClaims(tokenStr)\n\t\tif err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\n\t\t// Make claims available to all downstream handlers\n\t\tc.Locals(\"claims\", claims)\n\t\tc.Locals(\"user_id\", claims.UserID)\n\t\tc.Locals(\"role\", claims.Role)\n\t\treturn c.Next()\n\t}\n}\n\n// RoleRequired returns a middleware that allows only the specified roles.\n// Must be chained after BearerAuth.\nfunc RoleRequired(roles ...string) fiber.Handler {\n\tallowed := make(map[string]struct{}, len(roles))\n\tfor _, r := range roles {\n\t\tallowed[r] = struct{}{}\n\t}\n\treturn func(c *fiber.Ctx) error {\n\t\trole, _ := c.Locals(\"role\").(string)\n\t\tif _, ok := allowed[role]; !ok {\n\t\t\treturn c.Status(fiber.StatusForbidden).JSON(fiber.Map{\n\t\t\t\t\"error\": \"insufficient permissions\",\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\n"} {"category": "auth", "text": "\n go1.23\n controllers\n\n// controllers/auth_controller.go\n// Pattern: login + token generation handler\n// Observed in: Medical-App-Core/controllers/auth_controller.go\npackage controllers\n\nimport (\n\t\"net/http\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// AuthController handles token generation and validation endpoints.\ntype AuthController struct {\n\tauthSvc *implementations.AuthTokenServiceImpl\n}\n\nfunc NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {\n\treturn &AuthController{authSvc: svc}\n}\n\ntype generateTokenRequest struct {\n\tUserID string `json:\"user_id\" validate:\"required,uuid4\"`\n\tEmail string `json:\"email\" validate:\"required,email\"`\n\tRole string `json:\"role\" validate:\"required\"`\n}\n\n// GenerateToken godoc\n// @Summary Issue a JWT token\n// @Description Generates a signed JWT for the given user credentials\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body generateTokenRequest true \"Token request\"\n// @Success 200 {object} fiber.Map{token=string}\n// @Failure 400 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /auth/token [post]\nfunc (ac *AuthController) GenerateToken(c *fiber.Ctx) error {\n\tvar req generateTokenRequest\n\tif err := c.BodyParser(&req); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\tif req.UserID == \"\" || req.Email == \"\" || req.Role == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"user_id, email, and role are required\"})\n\t}\n\n\ttoken, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to generate token\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"token\": token})\n}\n\n// ValidateToken godoc\n// @Summary Validate a JWT token\n// @Description Returns 200 if the token is valid, 401 otherwise\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body fiber.Map{token=string} true \"Token to validate\"\n// @Success 200 {object} fiber.Map{valid=bool}\n// @Failure 401 {object} fiber.Map\n// @Router /auth/validate [post]\nfunc (ac *AuthController) ValidateToken(c *fiber.Ctx) error {\n\tvar body struct {\n\t\tToken string `json:\"token\"`\n\t}\n\tif err := c.BodyParser(&body); err != nil || body.Token == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"token is required\"})\n\t}\n\n\tif err := ac.authSvc.ValidateToken(body.Token); err != nil {\n\t\treturn c.Status(http.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\"valid\": false,\n\t\t\t\"reason\": err.Error(),\n\t\t})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"valid\": true})\n}\n\n"} {"category": "auth", "text": "\n go1.23\n initializers\n\n// initializers/validators.go\n// Pattern: custom validator registration with go-playground/validator\n// Observed in: Medical-App-Core/initializers/validators.go\npackage initializers\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\n// RegisterCustomValidators adds domain-specific validation rules.\nfunc RegisterCustomValidators(v *validator.Validate) {\n\t_ = v.RegisterValidation(\"cpf\", validateCPF)\n\t_ = v.RegisterValidation(\"cnpj\", validateCNPJ)\n\t_ = v.RegisterValidation(\"crm\", validateCRM)\n}\n\n// validateCPF validates a Brazilian CPF number (11 digits, checksum).\nfunc validateCPF(fl validator.FieldLevel) bool {\n\tcpf := strings.ReplaceAll(fl.Field().String(), \".\", \"\")\n\tcpf = strings.ReplaceAll(cpf, \"-\", \"\")\n\tif len(cpf) != 11 || allEqual(cpf) {\n\t\treturn false\n\t}\n\treturn cpfChecksum(cpf)\n}\n\nfunc cpfChecksum(cpf string) bool {\n\tdigits := make([]int, 11)\n\tfor i, ch := range cpf {\n\t\tdigits[i], _ = strconv.Atoi(string(ch))\n\t}\n\tfor pass := 0; pass < 2; pass++ {\n\t\tweight := 10 + pass\n\t\tsum := 0\n\t\tfor i := 0; i < 9+pass; i++ {\n\t\t\tsum += digits[i] * (weight - i)\n\t\t}\n\t\trem := (sum * 10) % 11\n\t\tif rem == 10 || rem == 11 {\n\t\t\trem = 0\n\t\t}\n\t\tif rem != digits[9+pass] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).\nfunc validateCNPJ(fl validator.FieldLevel) bool {\n\tcnpj := strings.Map(func(r rune) rune {\n\t\tif r >= '0' && r <= '9' { return r }\n\t\treturn -1\n\t}, fl.Field().String())\n\tif len(cnpj) != 14 || allEqual(cnpj) {\n\t\treturn false\n\t}\n\treturn cnpjChecksum(cnpj)\n}\n\nfunc cnpjChecksum(cnpj string) bool {\n\tweights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tweights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tfor pass, weights := range [][]int{weights1, weights2} {\n\t\tsum := 0\n\t\tfor i, w := range weights {\n\t\t\td, _ := strconv.Atoi(string(cnpj[i]))\n\t\t\tsum += d * w\n\t\t}\n\t\trem := sum % 11\n\t\texpected := byte('0')\n\t\tif rem >= 2 {\n\t\t\texpected = byte('0' + (11 - rem))\n\t\t}\n\t\tif cnpj[12+pass] != expected {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).\nfunc validateCRM(fl validator.FieldLevel) bool {\n\tcrm := fl.Field().String()\n\treturn len(crm) >= 4 && len(crm) <= 10\n}\n\nfunc allEqual(s string) bool {\n\tif s == \"\" { return false }\n\tfor _, c := range s[1:] {\n\t\tif byte(c) != s[0] { return false }\n\t}\n\treturn true\n}\n\n"} {"category": "auth", "text": "\n go1.24\n implementations\n\n// services/implementations/auth_service.go\n// Pattern: JWT HS256 token generation + validation service\n// Observed in: Medical-App-Core/services/implementations/auth_service.go\npackage implementations\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// Claims holds the JWT payload.\ntype Claims struct {\n\tUserID string `json:\"user_id\"`\n\tEmail string `json:\"email\"`\n\tRole string `json:\"role\"`\n\tjwt.RegisteredClaims\n}\n\n// AuthTokenServiceImpl handles JWT creation and validation.\ntype AuthTokenServiceImpl struct {\n\tsecret []byte\n\tttl time.Duration\n}\n\n// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.\nfunc NewAuthTokenService() *AuthTokenServiceImpl {\n\tsecret := os.Getenv(\"JWT_SECRET\")\n\tif secret == \"\" {\n\t\tpanic(\"JWT_SECRET environment variable must be set\")\n\t}\n\treturn &AuthTokenServiceImpl{\n\t\tsecret: []byte(secret),\n\t\tttl: 24 * time.Hour,\n\t}\n}\n\n// GenerateToken creates a signed HS256 JWT for the given user attributes.\nfunc (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {\n\tclaims := &Claims{\n\t\tUserID: userID,\n\t\tEmail: email,\n\t\tRole: role,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),\n\t\t\tIssuedAt: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer: \"medical-sas-api\",\n\t\t},\n\t}\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\tsigned, err := token.SignedString(s.secret)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"sign token: %w\", err)\n\t}\n\treturn signed, nil\n}\n\n// ValidateToken parses and validates a JWT string. Returns nil on success.\nfunc (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {\n\t\tif _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", t.Header[\"alg\"])\n\t\t}\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid token: %w\", err)\n\t}\n\tif !token.Valid {\n\t\treturn fmt.Errorf(\"token is not valid\")\n\t}\n\treturn nil\n}\n\n// ParseClaims extracts the Claims from a valid JWT string.\nfunc (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {\n\tclaims := &Claims{}\n\t_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {\n\t\treturn s.secret, nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse claims: %w\", err)\n\t}\n\treturn claims, nil\n}\n\n// HashPassword returns a bcrypt hash of the plain-text password.\nfunc HashPassword(plain string) (string, error) {\n\thashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"hash password: %w\", err)\n\t}\n\treturn string(hashed), nil\n}\n\n// CheckPassword returns nil if plain matches the bcrypt hash.\nfunc CheckPassword(plain, hash string) error {\n\tif err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {\n\t\treturn fmt.Errorf(\"invalid credentials\")\n\t}\n\treturn nil\n}\n\n"} {"category": "auth", "text": "\n go1.24\n middleware\n\n// middleware/auth.go\n// Pattern: Fiber JWT bearer middleware with claims propagation via Locals\n// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware\npackage middleware\n\nimport (\n\t\"strings\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// BearerAuth validates the Authorization: Bearer header and stores parsed\n// claims in fiber.Ctx.Locals(\"claims\") for downstream handlers.\nfunc BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {\n\treturn func(c *fiber.Ctx) error {\n\t\theader := c.Get(\"Authorization\")\n\t\tif !strings.HasPrefix(header, \"Bearer \") {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"missing or malformed Authorization header\",\n\t\t\t})\n\t\t}\n\n\t\ttokenStr := strings.TrimPrefix(header, \"Bearer \")\n\t\tclaims, err := authSvc.ParseClaims(tokenStr)\n\t\tif err != nil {\n\t\t\treturn c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\t\"error\": \"invalid or expired token\",\n\t\t\t\t\"reason\": err.Error(),\n\t\t\t})\n\t\t}\n\n\t\t// Make claims available to all downstream handlers\n\t\tc.Locals(\"claims\", claims)\n\t\tc.Locals(\"user_id\", claims.UserID)\n\t\tc.Locals(\"role\", claims.Role)\n\t\treturn c.Next()\n\t}\n}\n\n// RoleRequired returns a middleware that allows only the specified roles.\n// Must be chained after BearerAuth.\nfunc RoleRequired(roles ...string) fiber.Handler {\n\tallowed := make(map[string]struct{}, len(roles))\n\tfor _, r := range roles {\n\t\tallowed[r] = struct{}{}\n\t}\n\treturn func(c *fiber.Ctx) error {\n\t\trole, _ := c.Locals(\"role\").(string)\n\t\tif _, ok := allowed[role]; !ok {\n\t\t\treturn c.Status(fiber.StatusForbidden).JSON(fiber.Map{\n\t\t\t\t\"error\": \"insufficient permissions\",\n\t\t\t})\n\t\t}\n\t\treturn c.Next()\n\t}\n}\n\n"} {"category": "auth", "text": "\n go1.24\n controllers\n\n// controllers/auth_controller.go\n// Pattern: login + token generation handler\n// Observed in: Medical-App-Core/controllers/auth_controller.go\npackage controllers\n\nimport (\n\t\"net/http\"\n\n\t\"medical-sas-api/services/implementations\"\n\t\"github.com/gofiber/fiber/v2\"\n)\n\n// AuthController handles token generation and validation endpoints.\ntype AuthController struct {\n\tauthSvc *implementations.AuthTokenServiceImpl\n}\n\nfunc NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {\n\treturn &AuthController{authSvc: svc}\n}\n\ntype generateTokenRequest struct {\n\tUserID string `json:\"user_id\" validate:\"required,uuid4\"`\n\tEmail string `json:\"email\" validate:\"required,email\"`\n\tRole string `json:\"role\" validate:\"required\"`\n}\n\n// GenerateToken godoc\n// @Summary Issue a JWT token\n// @Description Generates a signed JWT for the given user credentials\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body generateTokenRequest true \"Token request\"\n// @Success 200 {object} fiber.Map{token=string}\n// @Failure 400 {object} fiber.Map\n// @Failure 500 {object} fiber.Map\n// @Router /auth/token [post]\nfunc (ac *AuthController) GenerateToken(c *fiber.Ctx) error {\n\tvar req generateTokenRequest\n\tif err := c.BodyParser(&req); err != nil {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"invalid request payload\"})\n\t}\n\tif req.UserID == \"\" || req.Email == \"\" || req.Role == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"user_id, email, and role are required\"})\n\t}\n\n\ttoken, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)\n\tif err != nil {\n\t\treturn c.Status(http.StatusInternalServerError).JSON(fiber.Map{\"error\": \"failed to generate token\"})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"token\": token})\n}\n\n// ValidateToken godoc\n// @Summary Validate a JWT token\n// @Description Returns 200 if the token is valid, 401 otherwise\n// @Tags Auth\n// @Accept json\n// @Produce json\n// @Param body body fiber.Map{token=string} true \"Token to validate\"\n// @Success 200 {object} fiber.Map{valid=bool}\n// @Failure 401 {object} fiber.Map\n// @Router /auth/validate [post]\nfunc (ac *AuthController) ValidateToken(c *fiber.Ctx) error {\n\tvar body struct {\n\t\tToken string `json:\"token\"`\n\t}\n\tif err := c.BodyParser(&body); err != nil || body.Token == \"\" {\n\t\treturn c.Status(http.StatusBadRequest).JSON(fiber.Map{\"error\": \"token is required\"})\n\t}\n\n\tif err := ac.authSvc.ValidateToken(body.Token); err != nil {\n\t\treturn c.Status(http.StatusUnauthorized).JSON(fiber.Map{\n\t\t\t\"valid\": false,\n\t\t\t\"reason\": err.Error(),\n\t\t})\n\t}\n\n\treturn c.Status(http.StatusOK).JSON(fiber.Map{\"valid\": true})\n}\n\n"} {"category": "auth", "text": "\n go1.24\n initializers\n\n// initializers/validators.go\n// Pattern: custom validator registration with go-playground/validator\n// Observed in: Medical-App-Core/initializers/validators.go\npackage initializers\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\n// RegisterCustomValidators adds domain-specific validation rules.\nfunc RegisterCustomValidators(v *validator.Validate) {\n\t_ = v.RegisterValidation(\"cpf\", validateCPF)\n\t_ = v.RegisterValidation(\"cnpj\", validateCNPJ)\n\t_ = v.RegisterValidation(\"crm\", validateCRM)\n}\n\n// validateCPF validates a Brazilian CPF number (11 digits, checksum).\nfunc validateCPF(fl validator.FieldLevel) bool {\n\tcpf := strings.ReplaceAll(fl.Field().String(), \".\", \"\")\n\tcpf = strings.ReplaceAll(cpf, \"-\", \"\")\n\tif len(cpf) != 11 || allEqual(cpf) {\n\t\treturn false\n\t}\n\treturn cpfChecksum(cpf)\n}\n\nfunc cpfChecksum(cpf string) bool {\n\tdigits := make([]int, 11)\n\tfor i, ch := range cpf {\n\t\tdigits[i], _ = strconv.Atoi(string(ch))\n\t}\n\tfor pass := 0; pass < 2; pass++ {\n\t\tweight := 10 + pass\n\t\tsum := 0\n\t\tfor i := 0; i < 9+pass; i++ {\n\t\t\tsum += digits[i] * (weight - i)\n\t\t}\n\t\trem := (sum * 10) % 11\n\t\tif rem == 10 || rem == 11 {\n\t\t\trem = 0\n\t\t}\n\t\tif rem != digits[9+pass] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).\nfunc validateCNPJ(fl validator.FieldLevel) bool {\n\tcnpj := strings.Map(func(r rune) rune {\n\t\tif r >= '0' && r <= '9' { return r }\n\t\treturn -1\n\t}, fl.Field().String())\n\tif len(cnpj) != 14 || allEqual(cnpj) {\n\t\treturn false\n\t}\n\treturn cnpjChecksum(cnpj)\n}\n\nfunc cnpjChecksum(cnpj string) bool {\n\tweights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tweights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}\n\tfor pass, weights := range [][]int{weights1, weights2} {\n\t\tsum := 0\n\t\tfor i, w := range weights {\n\t\t\td, _ := strconv.Atoi(string(cnpj[i]))\n\t\t\tsum += d * w\n\t\t}\n\t\trem := sum % 11\n\t\texpected := byte('0')\n\t\tif rem >= 2 {\n\t\t\texpected = byte('0' + (11 - rem))\n\t\t}\n\t\tif cnpj[12+pass] != expected {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).\nfunc validateCRM(fl validator.FieldLevel) bool {\n\tcrm := fl.Field().String()\n\treturn len(crm) >= 4 && len(crm) <= 10\n}\n\nfunc allEqual(s string) bool {\n\tif s == \"\" { return false }\n\tfor _, c := range s[1:] {\n\t\tif byte(c) != s[0] { return false }\n\t}\n\treturn true\n}\n\n"} {"category": "test", "text": "\n go1.21\n implementations_test\n\n// services/implementations/appointment_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestAppointmentService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\tsqlmock.AnyArg(), // patient_id\n\t\t\tsqlmock.AnyArg(), // doctor_id\n\t\t\tsqlmock.AnyArg(), // date_time\n\t\t\t\"scheduled\", // status\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_Create_DBError verifies the service surfaces DB errors.\nfunc TestAppointmentService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_FindByStatus_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestAppointmentService_FindByStatus_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"appointments\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByStatus(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.21\n controllers_test\n\n// controllers/appointment_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockAppointmentService implements contracts.AppointmentServiceContract for testing.\ntype mockAppointmentService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockAppointmentService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestAppointmentController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"patient_name\": \"João Silva\",\n\t\t\"doctor_full_name\": \"Dr. Maria\",\n\t\t\"specialization\": \"Cardiology\",\n\t\t\"date_time\": \"2024-03-01T10:00:00Z\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Appointment created successfully\", result[\"message\"])\n}\n\nfunc TestAppointmentController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestAppointmentController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.22\n implementations_test\n\n// services/implementations/appointment_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestAppointmentService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\tsqlmock.AnyArg(), // patient_id\n\t\t\tsqlmock.AnyArg(), // doctor_id\n\t\t\tsqlmock.AnyArg(), // date_time\n\t\t\t\"scheduled\", // status\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_Create_DBError verifies the service surfaces DB errors.\nfunc TestAppointmentService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_FindByStatus_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestAppointmentService_FindByStatus_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"appointments\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByStatus(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.22\n controllers_test\n\n// controllers/appointment_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockAppointmentService implements contracts.AppointmentServiceContract for testing.\ntype mockAppointmentService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockAppointmentService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestAppointmentController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"patient_name\": \"João Silva\",\n\t\t\"doctor_full_name\": \"Dr. Maria\",\n\t\t\"specialization\": \"Cardiology\",\n\t\t\"date_time\": \"2024-03-01T10:00:00Z\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Appointment created successfully\", result[\"message\"])\n}\n\nfunc TestAppointmentController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestAppointmentController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.23\n implementations_test\n\n// services/implementations/appointment_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestAppointmentService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\tsqlmock.AnyArg(), // patient_id\n\t\t\tsqlmock.AnyArg(), // doctor_id\n\t\t\tsqlmock.AnyArg(), // date_time\n\t\t\t\"scheduled\", // status\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_Create_DBError verifies the service surfaces DB errors.\nfunc TestAppointmentService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_FindByStatus_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestAppointmentService_FindByStatus_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"appointments\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByStatus(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.23\n controllers_test\n\n// controllers/appointment_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockAppointmentService implements contracts.AppointmentServiceContract for testing.\ntype mockAppointmentService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockAppointmentService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestAppointmentController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"patient_name\": \"João Silva\",\n\t\t\"doctor_full_name\": \"Dr. Maria\",\n\t\t\"specialization\": \"Cardiology\",\n\t\t\"date_time\": \"2024-03-01T10:00:00Z\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Appointment created successfully\", result[\"message\"])\n}\n\nfunc TestAppointmentController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestAppointmentController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.24\n implementations_test\n\n// services/implementations/appointment_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestAppointmentService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\tsqlmock.AnyArg(), // patient_id\n\t\t\tsqlmock.AnyArg(), // doctor_id\n\t\t\tsqlmock.AnyArg(), // date_time\n\t\t\t\"scheduled\", // status\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_Create_DBError verifies the service surfaces DB errors.\nfunc TestAppointmentService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tdto := contracts.AppointmentDTO{\n\t\tOrganizationID: uuid.New(),\n\t\tPatientID: uuid.New(),\n\t\tDoctorID: uuid.New(),\n\t\tDateTime: time.Now(),\n\t\tStatus: \"scheduled\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"appointments\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestAppointmentService_FindByStatus_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestAppointmentService_FindByStatus_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewAppointmentService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"appointments\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByStatus(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.24\n controllers_test\n\n// controllers/appointment_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockAppointmentService implements contracts.AppointmentServiceContract for testing.\ntype mockAppointmentService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockAppointmentService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestAppointmentController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"patient_name\": \"João Silva\",\n\t\t\"doctor_full_name\": \"Dr. Maria\",\n\t\t\"specialization\": \"Cardiology\",\n\t\t\"date_time\": \"2024-03-01T10:00:00Z\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Appointment created successfully\", result[\"message\"])\n}\n\nfunc TestAppointmentController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestAppointmentController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewAppointmentController(&mockAppointmentService{})\n\tapp.Post(\"/appointments\", ctrl.CreateAppointment)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/appointments\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.21\n implementations_test\n\n// services/implementations/patient_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestPatientService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\t\"João Silva\", // name\n\t\t\t\"+55 11 99999-9999\", // contact\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_Create_DBError verifies the service surfaces DB errors.\nfunc TestPatientService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_FindByCPF_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestPatientService_FindByCPF_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"patients\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByCPF(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.21\n controllers_test\n\n// controllers/patient_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockPatientService implements contracts.PatientServiceContract for testing.\ntype mockPatientService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockPatientService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestPatientController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewPatientController(&mockPatientService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"name\": \"João Silva\",\n\t\t\"contact\": \"+55 11 99999-9999\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Patient created successfully\", result[\"message\"])\n}\n\nfunc TestPatientController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestPatientController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.22\n implementations_test\n\n// services/implementations/patient_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestPatientService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\t\"João Silva\", // name\n\t\t\t\"+55 11 99999-9999\", // contact\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_Create_DBError verifies the service surfaces DB errors.\nfunc TestPatientService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_FindByCPF_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestPatientService_FindByCPF_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"patients\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByCPF(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.22\n controllers_test\n\n// controllers/patient_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockPatientService implements contracts.PatientServiceContract for testing.\ntype mockPatientService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockPatientService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestPatientController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewPatientController(&mockPatientService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"name\": \"João Silva\",\n\t\t\"contact\": \"+55 11 99999-9999\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Patient created successfully\", result[\"message\"])\n}\n\nfunc TestPatientController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestPatientController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.23\n implementations_test\n\n// services/implementations/patient_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestPatientService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\t\"João Silva\", // name\n\t\t\t\"+55 11 99999-9999\", // contact\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_Create_DBError verifies the service surfaces DB errors.\nfunc TestPatientService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_FindByCPF_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestPatientService_FindByCPF_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"patients\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByCPF(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.23\n controllers_test\n\n// controllers/patient_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockPatientService implements contracts.PatientServiceContract for testing.\ntype mockPatientService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockPatientService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestPatientController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewPatientController(&mockPatientService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"name\": \"João Silva\",\n\t\t\"contact\": \"+55 11 99999-9999\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Patient created successfully\", result[\"message\"])\n}\n\nfunc TestPatientController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestPatientController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.24\n implementations_test\n\n// services/implementations/patient_service_test.go\n// Pattern: sqlmock + GORM + testify — success and error paths\n// Observed in: Medical-App-Core/services/implementations/appointment_service_test.go\npackage implementations_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"medical-sas-api/services/contracts\"\n\t\"medical-sas-api/services/implementations\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n\t\"github.com/google/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\n// setupTestDB creates an in-memory mock database for service tests.\n// Returns the GORM DB and the sqlmock controller for expectation setup.\nfunc setupTestDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {\n\tt.Helper()\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create mock DB: %v\", err)\n\t}\n\tgormDB, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open GORM DB: %v\", err)\n\t}\n\tt.Cleanup(func() { db.Close() })\n\treturn gormDB, mock\n}\n\n// TestCreate_Success verifies that a valid DTO produces a non-nil UUID.\nfunc TestPatientService_Create_Success(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tnewID := uuid.New()\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWithArgs(\n\t\t\tsqlmock.AnyArg(), // created_at\n\t\t\tsqlmock.AnyArg(), // updated_at\n\t\t\tnil, // deleted_at\n\t\t\tsqlmock.AnyArg(), // organization_id\n\t\t\t\"João Silva\", // name\n\t\t\t\"+55 11 99999-9999\", // contact\n\t\t\tsqlmock.AnyArg(), // id\n\t\t).\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\"}).AddRow(newID))\n\tmock.ExpectCommit()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, newID, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_Create_DBError verifies the service surfaces DB errors.\nfunc TestPatientService_Create_DBError(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tdto := contracts.PatientDTO{\n\t\tOrganizationID: uuid.New().String(),\n\t\tName: \"João Silva\",\n\t\tContact: \"+55 11 99999-9999\",\n\t}\n\n\tmock.ExpectBegin()\n\tmock.ExpectQuery(`INSERT INTO \"patients\" .* RETURNING \"id\"`).\n\t\tWillReturnError(errors.New(\"connection refused\"))\n\tmock.ExpectRollback()\n\n\tgotID, err := svc.Create(dto)\n\n\tassert.Error(t, err)\n\tassert.Equal(t, uuid.Nil, gotID)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n// TestPatientService_FindByCPF_NotFound verifies gorm.ErrRecordNotFound is surfaced.\nfunc TestPatientService_FindByCPF_NotFound(t *testing.T) {\n\tdb, mock := setupTestDB(t)\n\tsvc := implementations.NewPatientService(db)\n\n\tmock.ExpectQuery(`SELECT .* FROM \"patients\"`).\n\t\tWithArgs(\"NOTEXIST\", sqlmock.AnyArg()).\n\t\tWillReturnError(gorm.ErrRecordNotFound)\n\n\tresult, err := svc.FindByCPF(\"NOTEXIST\")\n\n\tassert.Error(t, err)\n\tassert.Nil(t, result)\n\tassert.NoError(t, mock.ExpectationsWereMet())\n}\n\n"} {"category": "test", "text": "\n go1.24\n controllers_test\n\n// controllers/patient_controller_test.go\n// Pattern: Fiber app.Test() for HTTP handler unit testing\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage controllers_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/controllers\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// mockPatientService implements contracts.PatientServiceContract for testing.\ntype mockPatientService struct {\n\tcreateResult string\n\tcreateErr error\n}\n\nfunc (m *mockPatientService) CreateFromInput(input any) (any, error) {\n\treturn m.createResult, m.createErr\n}\n\nfunc TestPatientController_Create_Success(t *testing.T) {\n\tapp := fiber.New()\n\n\tctrl := controllers.NewPatientController(&mockPatientService{\n\t\tcreateResult: \"test-uuid\",\n\t})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\tbody, _ := json.Marshal(map[string]any{\n\t\t\"organization_name\": \"Test Org\",\n\t\t\"name\": \"João Silva\",\n\t\t\"contact\": \"+55 11 99999-9999\",\n\t})\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusCreated, resp.StatusCode)\n\n\tvar result map[string]any\n\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&result))\n\trequire.Equal(t, \"Patient created successfully\", result[\"message\"])\n}\n\nfunc TestPatientController_Create_MissingField(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\t// Send empty body — required fields missing\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"{}\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\nfunc TestPatientController_Create_InvalidJSON(t *testing.T) {\n\tapp := fiber.New()\n\tctrl := controllers.NewPatientController(&mockPatientService{})\n\tapp.Post(\"/patients\", ctrl.CreatePatient)\n\n\treq := httptest.NewRequest(http.MethodPost, \"/patients\", bytes.NewReader([]byte(\"not json\")))\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := app.Test(req, -1)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode)\n}\n\n"} {"category": "test", "text": "\n go1.21\n utils_test\n\n// utils/response_util_test.go\n// Pattern: table-driven tests with Fiber app.Test()\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage utils_test\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/utils\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRespondWithJSON(t *testing.T) {\n\tru := &utils.JSONResponseUtil{}\n\n\ttests := []struct {\n\t\tname string\n\t\tcode int\n\t\tpayload any\n\t\twantStatus int\n\t\twantKey string\n\t}{\n\t\t{\n\t\t\tname: \"200 ok with map payload\",\n\t\t\tcode: http.StatusOK,\n\t\t\tpayload: map[string]string{\"message\": \"ok\"},\n\t\t\twantStatus: http.StatusOK,\n\t\t\twantKey: \"message\",\n\t\t},\n\t\t{\n\t\t\tname: \"201 created\",\n\t\t\tcode: http.StatusCreated,\n\t\t\tpayload: map[string]string{\"id\": \"abc-123\"},\n\t\t\twantStatus: http.StatusCreated,\n\t\t\twantKey: \"id\",\n\t\t},\n\t\t{\n\t\t\tname: \"400 bad request\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t\tpayload: map[string]string{\"error\": \"invalid input\"},\n\t\t\twantStatus: http.StatusBadRequest,\n\t\t\twantKey: \"error\",\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tapp := fiber.New()\n\t\t\tapp.Get(\"/test\", func(c *fiber.Ctx) error {\n\t\t\t\treturn ru.RespondWithJSON(c, tc.code, tc.payload)\n\t\t\t})\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/test\", nil)\n\t\t\tresp, err := app.Test(req, -1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tc.wantStatus, resp.StatusCode)\n\t\t\trequire.Equal(t, \"application/json\", resp.Header.Get(\"Content-Type\"))\n\n\t\t\tvar body map[string]any\n\t\t\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&body))\n\t\t\t_, ok := body[tc.wantKey]\n\t\t\trequire.True(t, ok, \"expected key %q in response\", tc.wantKey)\n\t\t})\n\t}\n}\n\n"} {"category": "test", "text": "\n go1.22\n utils_test\n\n// utils/response_util_test.go\n// Pattern: table-driven tests with Fiber app.Test()\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage utils_test\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/utils\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRespondWithJSON(t *testing.T) {\n\tru := &utils.JSONResponseUtil{}\n\n\ttests := []struct {\n\t\tname string\n\t\tcode int\n\t\tpayload any\n\t\twantStatus int\n\t\twantKey string\n\t}{\n\t\t{\n\t\t\tname: \"200 ok with map payload\",\n\t\t\tcode: http.StatusOK,\n\t\t\tpayload: map[string]string{\"message\": \"ok\"},\n\t\t\twantStatus: http.StatusOK,\n\t\t\twantKey: \"message\",\n\t\t},\n\t\t{\n\t\t\tname: \"201 created\",\n\t\t\tcode: http.StatusCreated,\n\t\t\tpayload: map[string]string{\"id\": \"abc-123\"},\n\t\t\twantStatus: http.StatusCreated,\n\t\t\twantKey: \"id\",\n\t\t},\n\t\t{\n\t\t\tname: \"400 bad request\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t\tpayload: map[string]string{\"error\": \"invalid input\"},\n\t\t\twantStatus: http.StatusBadRequest,\n\t\t\twantKey: \"error\",\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tapp := fiber.New()\n\t\t\tapp.Get(\"/test\", func(c *fiber.Ctx) error {\n\t\t\t\treturn ru.RespondWithJSON(c, tc.code, tc.payload)\n\t\t\t})\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/test\", nil)\n\t\t\tresp, err := app.Test(req, -1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tc.wantStatus, resp.StatusCode)\n\t\t\trequire.Equal(t, \"application/json\", resp.Header.Get(\"Content-Type\"))\n\n\t\t\tvar body map[string]any\n\t\t\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&body))\n\t\t\t_, ok := body[tc.wantKey]\n\t\t\trequire.True(t, ok, \"expected key %q in response\", tc.wantKey)\n\t\t})\n\t}\n}\n\n"} {"category": "test", "text": "\n go1.23\n utils_test\n\n// utils/response_util_test.go\n// Pattern: table-driven tests with Fiber app.Test()\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage utils_test\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/utils\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRespondWithJSON(t *testing.T) {\n\tru := &utils.JSONResponseUtil{}\n\n\ttests := []struct {\n\t\tname string\n\t\tcode int\n\t\tpayload any\n\t\twantStatus int\n\t\twantKey string\n\t}{\n\t\t{\n\t\t\tname: \"200 ok with map payload\",\n\t\t\tcode: http.StatusOK,\n\t\t\tpayload: map[string]string{\"message\": \"ok\"},\n\t\t\twantStatus: http.StatusOK,\n\t\t\twantKey: \"message\",\n\t\t},\n\t\t{\n\t\t\tname: \"201 created\",\n\t\t\tcode: http.StatusCreated,\n\t\t\tpayload: map[string]string{\"id\": \"abc-123\"},\n\t\t\twantStatus: http.StatusCreated,\n\t\t\twantKey: \"id\",\n\t\t},\n\t\t{\n\t\t\tname: \"400 bad request\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t\tpayload: map[string]string{\"error\": \"invalid input\"},\n\t\t\twantStatus: http.StatusBadRequest,\n\t\t\twantKey: \"error\",\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tapp := fiber.New()\n\t\t\tapp.Get(\"/test\", func(c *fiber.Ctx) error {\n\t\t\t\treturn ru.RespondWithJSON(c, tc.code, tc.payload)\n\t\t\t})\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/test\", nil)\n\t\t\tresp, err := app.Test(req, -1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tc.wantStatus, resp.StatusCode)\n\t\t\trequire.Equal(t, \"application/json\", resp.Header.Get(\"Content-Type\"))\n\n\t\t\tvar body map[string]any\n\t\t\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&body))\n\t\t\t_, ok := body[tc.wantKey]\n\t\t\trequire.True(t, ok, \"expected key %q in response\", tc.wantKey)\n\t\t})\n\t}\n}\n\n"} {"category": "test", "text": "\n go1.24\n utils_test\n\n// utils/response_util_test.go\n// Pattern: table-driven tests with Fiber app.Test()\n// Observed in: Medical-App-Core/utils/response_util_test.go\npackage utils_test\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"medical-sas-api/utils\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRespondWithJSON(t *testing.T) {\n\tru := &utils.JSONResponseUtil{}\n\n\ttests := []struct {\n\t\tname string\n\t\tcode int\n\t\tpayload any\n\t\twantStatus int\n\t\twantKey string\n\t}{\n\t\t{\n\t\t\tname: \"200 ok with map payload\",\n\t\t\tcode: http.StatusOK,\n\t\t\tpayload: map[string]string{\"message\": \"ok\"},\n\t\t\twantStatus: http.StatusOK,\n\t\t\twantKey: \"message\",\n\t\t},\n\t\t{\n\t\t\tname: \"201 created\",\n\t\t\tcode: http.StatusCreated,\n\t\t\tpayload: map[string]string{\"id\": \"abc-123\"},\n\t\t\twantStatus: http.StatusCreated,\n\t\t\twantKey: \"id\",\n\t\t},\n\t\t{\n\t\t\tname: \"400 bad request\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t\tpayload: map[string]string{\"error\": \"invalid input\"},\n\t\t\twantStatus: http.StatusBadRequest,\n\t\t\twantKey: \"error\",\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tapp := fiber.New()\n\t\t\tapp.Get(\"/test\", func(c *fiber.Ctx) error {\n\t\t\t\treturn ru.RespondWithJSON(c, tc.code, tc.payload)\n\t\t\t})\n\n\t\t\treq := httptest.NewRequest(http.MethodGet, \"/test\", nil)\n\t\t\tresp, err := app.Test(req, -1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tc.wantStatus, resp.StatusCode)\n\t\t\trequire.Equal(t, \"application/json\", resp.Header.Get(\"Content-Type\"))\n\n\t\t\tvar body map[string]any\n\t\t\trequire.NoError(t, json.NewDecoder(resp.Body).Decode(&body))\n\t\t\t_, ok := body[tc.wantKey]\n\t\t\trequire.True(t, ok, \"expected key %q in response\", tc.wantKey)\n\t\t})\n\t}\n}\n\n"} {"category": "docker", "text": "\n go1.21\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.21 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 8080\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"8080\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:8080/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.21\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"8080:8080\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=8080\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.21\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.21 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3040\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3040\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3040/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.21\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3040:3040\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3040\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.21\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.21 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3000\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3000\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3000/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.21\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3000:3000\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3000\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.21\n shell\n\n#!/bin/sh\n# docker/api/entrypoint.sh\n# Pattern: validate required env vars before exec'ing the binary\n# Observed in: Medical-App-Core/docker/api/entrypoint.sh\nset -e\n\necho \"Checking required environment variables...\"\n\nMISSING=0\nfor VAR in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME JWT_SECRET; do\n if [ -z \"$(eval echo \\$${VAR})\" ]; then\n echo \"ERROR: $VAR is not set\"\n MISSING=1\n else\n echo \"OK: $VAR is set\"\n fi\ndone\n\nif [ \"$MISSING\" -eq 1 ]; then\n echo \"One or more required environment variables are missing. Aborting.\"\n exit 1\nfi\n\necho \"All required variables present. Starting application...\"\nexec ./app\n\n"} {"category": "docker", "text": "\n go1.21\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/my-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.21\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.21\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/medical-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.21\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.21\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/ecommerce-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.21\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.22\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.22 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 8080\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"8080\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:8080/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.22\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"8080:8080\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=8080\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.22\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.22 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3040\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3040\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3040/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.22\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3040:3040\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3040\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.22\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.22 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3000\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3000\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3000/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.22\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3000:3000\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3000\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.22\n shell\n\n#!/bin/sh\n# docker/api/entrypoint.sh\n# Pattern: validate required env vars before exec'ing the binary\n# Observed in: Medical-App-Core/docker/api/entrypoint.sh\nset -e\n\necho \"Checking required environment variables...\"\n\nMISSING=0\nfor VAR in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME JWT_SECRET; do\n if [ -z \"$(eval echo \\$${VAR})\" ]; then\n echo \"ERROR: $VAR is not set\"\n MISSING=1\n else\n echo \"OK: $VAR is set\"\n fi\ndone\n\nif [ \"$MISSING\" -eq 1 ]; then\n echo \"One or more required environment variables are missing. Aborting.\"\n exit 1\nfi\n\necho \"All required variables present. Starting application...\"\nexec ./app\n\n"} {"category": "docker", "text": "\n go1.22\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/my-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.22\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.22\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/medical-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.22\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.22\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/ecommerce-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.22\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.23\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.23 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 8080\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"8080\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:8080/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.23\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"8080:8080\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=8080\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.23\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.23 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3040\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3040\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3040/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.23\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3040:3040\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3040\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.23\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.23 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3000\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3000\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3000/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.23\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3000:3000\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3000\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.23\n shell\n\n#!/bin/sh\n# docker/api/entrypoint.sh\n# Pattern: validate required env vars before exec'ing the binary\n# Observed in: Medical-App-Core/docker/api/entrypoint.sh\nset -e\n\necho \"Checking required environment variables...\"\n\nMISSING=0\nfor VAR in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME JWT_SECRET; do\n if [ -z \"$(eval echo \\$${VAR})\" ]; then\n echo \"ERROR: $VAR is not set\"\n MISSING=1\n else\n echo \"OK: $VAR is set\"\n fi\ndone\n\nif [ \"$MISSING\" -eq 1 ]; then\n echo \"One or more required environment variables are missing. Aborting.\"\n exit 1\nfi\n\necho \"All required variables present. Starting application...\"\nexec ./app\n\n"} {"category": "docker", "text": "\n go1.23\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/my-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.23\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.23\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/medical-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.23\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.23\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/ecommerce-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.23\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.24\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.24 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 8080\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"8080\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:8080/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.24\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"8080:8080\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=8080\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.24\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.24 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3040\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3040\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3040/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.24\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3040:3040\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3040\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.24\n dockerfile\n\n# Dockerfile — multi-stage build for a Go Fiber application\n# Pattern: golang builder + debian:bookworm-slim runtime\n# Observed in: Medical-App-Core/Dockerfile\n\n# ─── Stage 1: Build ────────────────────────────────────────────────────────\nFROM golang:1.24 AS builder\n\nWORKDIR /app\n\n# Copy dependency manifests first for better layer caching.\n# Only re-downloads modules when go.mod or go.sum changes.\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# Copy source and build the binary from cmd/\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags=\"-s -w\" -o app ./cmd\n\n# ─── Stage 2: Runtime ──────────────────────────────────────────────────────\nFROM debian:bookworm-slim\n\nWORKDIR /app\n\n# CA certificates are required for outbound HTTPS (e.g., Stripe API).\nRUN apt-get update && \\\n apt-get install -y --no-install-recommends ca-certificates curl && \\\n rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /app/app .\n\nEXPOSE 3000\n\n# Document all required environment variables (values supplied at runtime).\nENV DB_HOST=\"\" \\\n DB_PORT=\"5432\" \\\n DB_USER=\"\" \\\n DB_PASSWORD=\"\" \\\n DB_NAME=\"\" \\\n DB_TIMEZONE=\"UTC\" \\\n JWT_SECRET=\"\" \\\n RABBITMQ_BASE_URL=\"\" \\\n SWAGGER_USER=\"\" \\\n SWAGGER_PASSWORD=\"\" \\\n PORT=\"3000\"\n\nHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n CMD curl -f http://localhost:3000/health || exit 1\n\nCOPY ./docker/api/entrypoint.sh /entrypoint.sh\nRUN chmod +x /entrypoint.sh /app/app\n\nENTRYPOINT [\"/entrypoint.sh\"]\n\n"} {"category": "docker", "text": "\n go1.24\n docker-compose\n\n# docker-compose.yml — local development environment\n# Pattern: app + postgres + rabbitmq services with health checks\n# Observed from: Medical-App-Core infrastructure patterns\nversion: \"3.9\"\n\nservices:\n api:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"3000:3000\"\n environment:\n - DB_HOST=postgres\n - DB_PORT=5432\n - DB_USER=postgres\n - DB_PASSWORD=postgres\n - DB_NAME=appdb\n - DB_TIMEZONE=UTC\n - JWT_SECRET=local-dev-secret-change-in-prod\n - RABBITMQ_BASE_URL=amqp://guest:guest@rabbitmq:5672/\n - SWAGGER_USER=admin\n - SWAGGER_PASSWORD=admin\n - PORT=3000\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n restart: unless-stopped\n\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: postgres\n POSTGRES_DB: appdb\n ports:\n - \"5432:5432\"\n volumes:\n - pg_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgres\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\n rabbitmq:\n image: rabbitmq:3.13-management-alpine\n ports:\n - \"5672:5672\"\n - \"15672:15672\"\n healthcheck:\n test: [\"CMD\", \"rabbitmq-diagnostics\", \"ping\"]\n interval: 10s\n timeout: 5s\n retries: 5\n\nvolumes:\n pg_data:\n\n"} {"category": "docker", "text": "\n go1.24\n shell\n\n#!/bin/sh\n# docker/api/entrypoint.sh\n# Pattern: validate required env vars before exec'ing the binary\n# Observed in: Medical-App-Core/docker/api/entrypoint.sh\nset -e\n\necho \"Checking required environment variables...\"\n\nMISSING=0\nfor VAR in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME JWT_SECRET; do\n if [ -z \"$(eval echo \\$${VAR})\" ]; then\n echo \"ERROR: $VAR is not set\"\n MISSING=1\n else\n echo \"OK: $VAR is set\"\n fi\ndone\n\nif [ \"$MISSING\" -eq 1 ]; then\n echo \"One or more required environment variables are missing. Aborting.\"\n exit 1\nfi\n\necho \"All required variables present. Starting application...\"\nexec ./app\n\n"} {"category": "docker", "text": "\n go1.24\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/my-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.24\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.24\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/medical-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.24\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"} {"category": "docker", "text": "\n go1.24\n jenkins\n\n// Jenkinsfile — CI/CD pipeline\n// Pattern: multi-stage pipeline: test → build → docker → deploy\n// Observed in: Medical-App-Core/Jenkinsfile\npipeline {\n agent any\n\n environment {\n DOCKER_IMAGE = \"myorg/ecommerce-api\"\n DOCKER_TAG = \"${BUILD_NUMBER}\"\n GO_VERSION = \"1.24\"\n }\n\n stages {\n stage('Checkout') {\n steps {\n checkout scm\n }\n }\n\n stage('Test') {\n steps {\n sh '''\n go test ./... -v -coverprofile=coverage.out\n go tool cover -html=coverage.out -o coverage.html\n '''\n }\n post {\n always {\n publishHTML(target: [\n reportName: 'Go Coverage',\n reportDir: '.',\n reportFiles: 'coverage.html',\n ])\n }\n }\n }\n\n stage('Lint') {\n steps {\n sh 'go vet ./...'\n }\n }\n\n stage('Build Binary') {\n steps {\n sh 'CGO_ENABLED=0 GOOS=linux go build -o app ./cmd'\n }\n }\n\n stage('Docker Build') {\n steps {\n sh 'docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} .'\n sh 'docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest'\n }\n }\n\n stage('Docker Push') {\n steps {\n withCredentials([usernamePassword(\n credentialsId: 'dockerhub-creds',\n usernameVariable: 'DOCKER_USER',\n passwordVariable: 'DOCKER_PASS'\n )]) {\n sh '''\n echo \"$DOCKER_PASS\" | docker login -u \"$DOCKER_USER\" --password-stdin\n docker push ${DOCKER_IMAGE}:${DOCKER_TAG}\n docker push ${DOCKER_IMAGE}:latest\n '''\n }\n }\n }\n }\n\n post {\n always {\n sh 'docker system prune -f || true'\n }\n failure {\n echo \"Pipeline failed — check logs above\"\n }\n }\n}\n\n"}