Skip to content

Commit bfa0ae4

Browse files
add golang rest-api sample
1 parent 80560cb commit bfa0ae4

26 files changed

+1492
-0
lines changed

go/rest-api/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/main
2+
.DS_Store

go/rest-api/Dockerfile

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM golang:1.18.0-alpine as build-env
2+
3+
RUN mkdir /app
4+
WORKDIR /app
5+
COPY go.mod go.sum ./
6+
7+
# Get dependancies - will also be cached if we won't change mod/sum
8+
RUN go mod download
9+
10+
# COPY the source code as the last step
11+
COPY . .
12+
13+
14+
# Build the binary
15+
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
16+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o /go/bin/app -buildvcs=false
17+
18+
FROM alpine
19+
COPY --from=build-env /go/bin/app /go/bin/app
20+
USER 10014
21+
ENTRYPOINT ["/go/bin/app"]

go/rest-api/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Choreo Samples
2+
3+
## Sample Go API
4+
5+
#### Use the following build config when creating this component in Choreo:
6+
7+
- Dockerfile: `go/rest-api/Dockerfile`
8+
- Docker context: `go/rest-api/`
9+
- Port: `8080` (or set env var `PORT`)
10+
- OpenAPI filepath: `go/rest-api/docs/swagger.json`
11+
12+
#### Go build & run
13+
14+
```shell
15+
go build main.go && go run main.go
16+
```
17+
18+
#### Generate API definitions
19+
20+
Generated using Go annotations https://github.com/swaggo/swag
21+
22+
```shell
23+
swag init
24+
```
25+
26+
#### Load initial data ( optional )
27+
28+
1. Set env var in Choreo DevOps portal `INIT_DATA_PATH=<some_path>`
29+
2. Mount the file contents of `configs/initial_data.json` in the path specified in step 1.

go/rest-api/api/routes/api_v1.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package routes
2+
3+
import (
4+
"github.com/gofiber/fiber/v2"
5+
)
6+
7+
func Initialize(app *fiber.App) {
8+
initControllers()
9+
10+
RegisterHealthRoutes(app)
11+
apiVersion := app.Group("/api/v1")
12+
registerPetRoutes(apiVersion)
13+
registerCategoryRoutes(apiVersion)
14+
}

go/rest-api/api/routes/category.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package routes
2+
3+
import (
4+
"github.com/gofiber/fiber/v2"
5+
6+
"example.choreo.dev/internal/controllers"
7+
"example.choreo.dev/internal/utils"
8+
)
9+
10+
func registerCategoryRoutes(router fiber.Router) {
11+
r := router.Group("/category")
12+
r.Post("/", CreateCategory)
13+
r.Get("/", ListCategories)
14+
}
15+
16+
// CreateCategory
17+
// @Summary Add pet to the store
18+
// @Accept json
19+
// @Produce json
20+
// @Param request body controllers.AddCategoryRequest true "pet details"
21+
// @Router /api/v1/category [post]
22+
// @Failure 409 {object} any
23+
// @Success 200 {object} controllers.AddCategoryResponse "ok"
24+
func CreateCategory(c *fiber.Ctx) error {
25+
ctx := utils.GetRequestContext(c)
26+
var req controllers.AddCategoryRequest
27+
28+
if err := c.BodyParser(&req); err != nil {
29+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
30+
"status": "error",
31+
"details": "failed to parse payload",
32+
})
33+
}
34+
35+
res, err := categoryController.AddCategory(ctx, req)
36+
if err != nil {
37+
return err
38+
}
39+
return c.Status(fiber.StatusOK).JSON(res)
40+
}
41+
42+
// ListCategories
43+
// @Summary List available categories
44+
// @Produce json
45+
// @Router /api/v1/category [get]
46+
// @Success 200 {object} controllers.ListCategoriesResponse "ok"
47+
func ListCategories(c *fiber.Ctx) error {
48+
ctx := utils.GetRequestContext(c)
49+
50+
res, err := categoryController.ListCategories(ctx)
51+
if err != nil {
52+
return err
53+
}
54+
return c.Status(fiber.StatusOK).JSON(res)
55+
}

go/rest-api/api/routes/healthz.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package routes
2+
3+
import (
4+
"time"
5+
6+
"github.com/gofiber/fiber/v2"
7+
8+
"example.choreo.dev/internal/config"
9+
)
10+
11+
// @Summary Get pet info by id
12+
// @Produce json
13+
// @Router /healthz [post]
14+
// @Success 200 {object} map[string]any "ok"
15+
func handleHealthCheckRequest(ctx *fiber.Ctx) error {
16+
return ctx.JSON(fiber.Map{
17+
"message": "choreo-example-app is healthy",
18+
"env": config.GetConfig().Env,
19+
"timestamp": time.Now(),
20+
})
21+
}
22+
23+
func RegisterHealthRoutes(r fiber.Router) {
24+
r.Get("/healthz", handleHealthCheckRequest)
25+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package routes
2+
3+
import (
4+
"example.choreo.dev/internal/config"
5+
"example.choreo.dev/internal/controllers"
6+
"example.choreo.dev/internal/repositories"
7+
)
8+
9+
var petController *controllers.PetController
10+
var categoryController *controllers.CategoryController
11+
12+
func initControllers() {
13+
initialData := config.LoadInitialData()
14+
categoryRepository := repositories.NewCategoryRepository(initialData.Categories)
15+
petRepository := repositories.NewPetRepository(initialData.Pets)
16+
petController = controllers.NewPetController(petRepository, categoryRepository)
17+
categoryController = controllers.NewCategoryController(categoryRepository)
18+
}

go/rest-api/api/routes/pet.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package routes
2+
3+
import (
4+
"github.com/gofiber/fiber/v2"
5+
6+
"example.choreo.dev/internal/controllers"
7+
"example.choreo.dev/internal/utils"
8+
)
9+
10+
func registerPetRoutes(router fiber.Router) {
11+
r := router.Group("/pet")
12+
r.Post("/", CreatePet)
13+
r.Get("/:id", GetPet)
14+
}
15+
16+
// CreatePet
17+
// @Summary Add pet to the store
18+
// @Accept json
19+
// @Produce json
20+
// @Param request body controllers.AddPetRequest true "pet details"
21+
// @Router /api/v1/pet [post]
22+
// @Success 200 {object} controllers.AddPetResponse "ok"
23+
func CreatePet(c *fiber.Ctx) error {
24+
ctx := utils.GetRequestContext(c)
25+
var req controllers.AddPetRequest
26+
27+
if err := c.BodyParser(&req); err != nil {
28+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
29+
"status": "error",
30+
"details": "failed to parse payload",
31+
})
32+
}
33+
34+
res, err := petController.AddPet(ctx, req)
35+
if err != nil {
36+
return err
37+
}
38+
return c.Status(fiber.StatusOK).JSON(res)
39+
}
40+
41+
// GetPet
42+
// @Summary Get pet info by id
43+
// @Produce json
44+
// @Param id path string true "Pet ID"
45+
// @Router /api/v1/pet/{id} [get]
46+
// @Success 200 {object} controllers.GetPetByIdResponse "ok"
47+
func GetPet(c *fiber.Ctx) error {
48+
ctx := utils.GetRequestContext(c)
49+
id := c.Params("id")
50+
51+
res, err := petController.GetPetById(ctx, id)
52+
if err != nil {
53+
return err
54+
}
55+
return c.Status(fiber.StatusOK).JSON(res)
56+
}

go/rest-api/configs/initial_data.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"categories": [
3+
{
4+
"id": "10b42501-aead-4038-b548-fad045602d96",
5+
"name": "c1"
6+
},
7+
{
8+
"id": "86ea4178-637e-441f-9603-743094a2d1b9",
9+
"name": "c2"
10+
}
11+
],
12+
"pets": [
13+
{
14+
"id": "d78bf645-0000-4a2d-9534-78bcb0b9f393",
15+
"name": "p1",
16+
"category_id": "45dc4abb-1625-4c8c-85c1-4fcabda18873",
17+
"available": false
18+
},
19+
{
20+
"id": "8cd7bacd-16dd-4b1a-96d6-1ca09e720d41",
21+
"name": "p2",
22+
"category_id": "45dc4abb-1625-4c8c-85c1-4fcabda18873",
23+
"available": false
24+
},
25+
{
26+
"id": "d6ebd120-2b21-4210-a482-c919c90eda73",
27+
"name": "p3",
28+
"category_id": "45dc4abb-1625-4c8c-85c1-4fcabda18873",
29+
"available": false
30+
}
31+
]
32+
}

0 commit comments

Comments
 (0)