git clone https://github.com/go-tutorials/go-firestore-sample
cd go-firestore-samplego run main.go- GET: retrieve a representation of the resource
- POST: create a new resource
- PUT: update the resource
- PATCH: perform a partial update of a resource
- DELETE: delete a resource
To check if the service is available.
{
    "status": "UP",
    "details": {
        "firestore": {
            "status": "UP"
        }
    }
}[
    {
        "id": "spiderman",
        "username": "peter.parker",
        "email": "[email protected]",
        "phone": "0987654321",
        "dateOfBirth": "1962-08-25T16:59:59.999Z"
    },
    {
        "id": "wolverine",
        "username": "james.howlett",
        "email": "[email protected]",
        "phone": "0987654321",
        "dateOfBirth": "1974-11-16T16:59:59.999Z"
    }
]GET /users/wolverine{
    "id": "wolverine",
    "username": "james.howlett",
    "email": "[email protected]",
    "phone": "0987654321",
    "dateOfBirth": "1974-11-16T16:59:59.999Z"
}{
    "id": "wolverine",
    "username": "james.howlett",
    "email": "[email protected]",
    "phone": "0987654321",
    "dateOfBirth": "1974-11-16T16:59:59.999Z"
}1PUT /users/wolverine{
    "username": "james.howlett",
    "email": "[email protected]",
    "phone": "0987654321",
    "dateOfBirth": "1974-11-16T16:59:59.999Z"
}1Perform a partial update of user. For example, if you want to update 2 fields: email and phone, you can send the request body of below.
PATCH /users/wolverine{
    "email": "[email protected]",
    "phone": "0987654321"
}1If we pass a struct as a parameter, we cannot control what fields we need to update. So, we must pass a map as a parameter.
type UserService interface {
	Update(ctx context.Context, user *User) (int64, error)
	Patch(ctx context.Context, user map[string]interface{}) (int64, error)
}We must solve 2 problems:
- At http handler layer, we must convert the user struct to map, with json format, and make sure the nested data types are passed correctly.
- At service layer or repository layer, from json format, we must convert the json format to database format (in this case, we must convert to bson of Mongo)
- At http handler layer, we use core-go/core, to convert the user struct to map, to make sure we just update the fields we need to update
import server "github.com/core-go/core"
func (h *UserHandler) Patch(w http.ResponseWriter, r *http.Request) {
    var user User
    userType := reflect.TypeOf(user)
    _, jsonMap := sv.BuildMapField(userType)
    body, _ := sv.BuildMapAndStruct(r, &user)
    json, er1 := sv.BodyToJson(r, user, body, ids, jsonMap, nil)
    result, er2 := h.service.Patch(r.Context(), json)
    if er2 != nil {
        http.Error(w, er2.Error(), http.StatusInternalServerError)
        return
    }
    respond(w, result)
}DELETE /users/wolverine1- core-go/health: include Health Handler, Firestore Health Checker
- core-go/config: to load the config file, and merge with other environments (SIT, UAT, ENV)
- core-go/log: log and log middleware
To check if the service is available, refer to core-go/health
{
    "status": "UP",
    "details": {
        "firestore": {
            "status": "UP"
        }
    }
}To create health checker, and health handler
	opts := option.WithCredentialsJSON([]byte(root.Credentials))
	app, er1 := firebase.NewApp(ctx, nil, opts)
	if er1 != nil {
		return nil, er1
	}
	client, er2 := app.Firestore(ctx)
	if er2 != nil {
		return nil, er2
	}
	userService := services.NewUserService(client)
	userHandler := handlers.NewUserHandler(userService)
	firestoreChecker := firestore.NewHealthChecker(ctx, []byte(root.Credentials))
	healthHandler := health.NewHealthHandler(firestoreChecker)To handler routing
	r := mux.NewRouter()
	r.HandleFunc("/health", healthHandler.Check).Methods("GET")To load the config from "config.yml", in "configs" folder
package app
import (
	"github.com/core-go/log"
	mid "github.com/core-go/log/middleware"
)
type Root struct {
	Server      ServerConfig  `mapstructure:"server"`
	Log         log.Config    `mapstructure:"log"`
	MiddleWare  mid.LogConfig `mapstructure:"middleware"`
	Credentials string        `mapstructure:"credentials"`
}
type ServerConfig struct {
	Name string `mapstructure:"name"`
	Port int64  `mapstructure:"port"`
}import (
	"github.com/core-go/config"
	"github.com/core-go/log"
	mid "github.com/core-go/log/middleware"
	"github.com/gorilla/mux"
)
func main() {
	var conf app.Root
	config.Load(&conf, "configs/config")
	r := mux.NewRouter()
	log.Initialize(conf.Log)
	r.Use(mid.BuildContext)
	logger := mid.NewStructuredLogger()
	r.Use(mid.Logger(conf.MiddleWare, log.InfoFields, logger))
	r.Use(mid.Recover(log.ErrorMsg))
}To configure to ignore the health check, use "skips":
middleware:
  skips: /health
