Skip to content

Commit

Permalink
Merge pull request #24 from subomi/subomi/docs/improve-documentation
Browse files Browse the repository at this point in the history
docs: improved README and changed SetBody to Write
  • Loading branch information
subomi authored Jun 23, 2024
2 parents fb3a6ee + 923c755 commit 35de2a3
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 66 deletions.
31 changes: 12 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# requestmigrations <br /> [![Go Reference](https://pkg.go.dev/badge/github.com/subomi/requestmigrations.svg)](https://pkg.go.dev/github.com/subomi/requestmigrations)
`requestmigrations` is a Golang implementation of [rolling versions](https://stripe.com/blog/api-versioning) for REST APIs. It's a port of the [Ruby implementation](https://github.com/keygen-sh/request_migrations) by [ezekg](https://github.com/ezekg).
`requestmigrations` is a Golang implementation of [rolling versions](https://stripe.com/blog/api-versioning) for REST APIs. It's a port of the [Ruby implementation](https://github.com/keygen-sh/request_migrations) by [ezekg](https://github.com/ezekg). We use in production with [Convoy](https://github.com/frain-dev/convoy).

## Features
- API Versioning with date and semver versioning support.
Expand All @@ -12,41 +12,34 @@
```

## Usage
This package exposes primarily one API - `Migrate`. It is used to migrate and rollback changes to your request and response respectively. Here's a short exmaple:
This package exposes primarily one API - `Migrate`. It is used to migrate and rollback changes to your request and response respectively. Here's a short example:

```go
package main

func createUser(r *http.Request, w http.ResponseWriter) {
err, res, rollback := rm.Migrate(r, "createUser")
// Identify version and transform the request payload.
err, vw, rollback := rm.Migrate(r, "createUser")
if err != nil {
w.Write("Bad Request")
}
defer rollback(w)

payload, err := io.ReadAll(r.Body)
if err != nil {
w.Write("Bad Request")
}
// Setup response transformation callback.
defer rollback(w)

var userObject user
err = json.Unmarshal(payload, &userObject)
// ...Perform core business logic...
data, err := createUserObject(body)
if err != nil {
w.Write("Bad Request")
}

userObject = user{
Email: userObject.Email,
FirstName: userObject.FirstName,
LastName: userObject.LastName,
return err
}

body, err := json.Marshal(userObject)
// Write response
body, err := json.Marshal(data)
if err != nil {
w.Write("Bad Request")
}

res.SetBody(body)
vw.Write(body)
}

```
Expand Down
7 changes: 7 additions & 0 deletions example/advanced/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fmt"

func main() {
fmt.Println("TODO: Write a more complex example")
}
29 changes: 29 additions & 0 deletions example/basic/helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package helper

import "encoding/json"

type ServerResponse struct {
Status bool `json:"status"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
}

func GenerateSuccessResponse(payload interface{}, message string) ([]byte, error) {
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}

s := &ServerResponse{
Status: true,
Message: message,
Data: data,
}

res, err := json.Marshal(s)
if err != nil {
return nil, err
}

return res, nil
}
53 changes: 18 additions & 35 deletions example/basic/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import (
"encoding/json"
"basicexample/helper"
v20230401 "basicexample/v20230401"
v20230501 "basicexample/v20230501"
"log"
"math/rand"
"net/http"
Expand All @@ -27,12 +29,12 @@ func main() {
log.Fatal(err)
}

rm.RegisterMigrations(rms.Migrations{
rm.RegisterMigrations(rms.MigrationStore{
"2023-05-01": []rms.Migration{
&expandProfileForUserMigration{},
&v20230501.ListUserResponseMigration{},
},
"2023-04-01": []rms.Migration{
&combineNamesForUserMigration{},
&v20230401.ListUserResponseMigration{},
},
})

Expand All @@ -55,8 +57,7 @@ func main() {
func buildMux(api *API) http.Handler {
m := mux.NewRouter()

m.Handle("/users",
api.rm.VersionAPI(http.HandlerFunc(api.ListUser))).Methods("GET")
m.HandleFunc("/users", api.ListUser).Methods("GET")
m.HandleFunc("/users/{id}", api.GetUser).Methods("GET")

reg := prometheus.NewRegistry()
Expand All @@ -69,11 +70,6 @@ func buildMux(api *API) http.Handler {
}

// api models
type ServerResponse struct {
Status bool `json:"status"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
}

// define api
type API struct {
Expand All @@ -82,6 +78,14 @@ type API struct {
}

func (a *API) ListUser(w http.ResponseWriter, r *http.Request) {
err, vw, rollback := a.rm.Migrate(r, "ListUser")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

defer rollback(w)

// Generate a random Int type number between 1 and 10
randNum := rand.Intn(2-1+1) + 1
time.Sleep(time.Duration(randNum) * time.Second)
Expand All @@ -92,13 +96,13 @@ func (a *API) ListUser(w http.ResponseWriter, r *http.Request) {
return
}

res, err := generateSuccessResponse(users, "users retrieved successfully")
res, err := helper.GenerateSuccessResponse(users, "users retrieved successfully")
if err != nil {
w.WriteHeader(http.StatusBadGateway)
return
}

w.Write(res)
vw.Write(res)
}

func (a *API) GetUser(w http.ResponseWriter, r *http.Request) {
Expand All @@ -109,32 +113,11 @@ func (a *API) GetUser(w http.ResponseWriter, r *http.Request) {
return
}

res, err := generateSuccessResponse(user, "user retrieved successfully")
res, err := helper.GenerateSuccessResponse(user, "user retrieved successfully")
if err != nil {
w.WriteHeader(http.StatusBadGateway)
return
}

w.Write(res)
}

// helpers
func generateSuccessResponse(payload interface{}, message string) ([]byte, error) {
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}

s := &ServerResponse{
Status: true,
Message: message,
Data: data,
}

res, err := json.Marshal(s)
if err != nil {
return nil, err
}

return res, nil
}
15 changes: 8 additions & 7 deletions example/basic/requestmigrations.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"basicexample/helper"
"encoding/json"
"net/http"
"net/url"
Expand All @@ -9,9 +10,9 @@ import (
)

// Migrations
type combineNamesForUserMigration struct{}
type ListUserResponseMigration struct{}

func (c *combineNamesForUserMigration) ShouldMigrateConstraint(
func (c *ListUserResponseMigration) ShouldMigrateConstraint(
url *url.URL,
method string,
data []byte,
Expand All @@ -24,7 +25,7 @@ func (c *combineNamesForUserMigration) ShouldMigrateConstraint(
return isUserPath && isGetMethod && isValidType
}

func (c *combineNamesForUserMigration) Migrate(
func (c *ListUserResponseMigration) Migrate(
body []byte,
h http.Header) ([]byte, http.Header, error) {
type oldUser struct {
Expand All @@ -36,7 +37,7 @@ func (c *combineNamesForUserMigration) Migrate(
UpdatedAt time.Time `json:"updated_at"`
}

var res ServerResponse
var res helper.ServerResponse
err := json.Unmarshal(body, &res)
if err != nil {
return nil, nil, err
Expand All @@ -60,7 +61,7 @@ func (c *combineNamesForUserMigration) Migrate(
newUsers = append(newUsers, &oldUser)
}

body, err = generateSuccessResponse(&newUsers, "users retrieved successfully")
body, err = helper.GenerateSuccessResponse(&newUsers, "users retrieved successfully")
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -96,7 +97,7 @@ func (e *expandProfileForUserMigration) ShouldMigrateConstraint(
func (e *expandProfileForUserMigration) Migrate(
body []byte,
h http.Header) ([]byte, http.Header, error) {
var res ServerResponse
var res helper.ServerResponse
err := json.Unmarshal(body, &res)
if err != nil {
return nil, nil, err
Expand All @@ -121,7 +122,7 @@ func (e *expandProfileForUserMigration) Migrate(
newUsers = append(newUsers, &oldUser)
}

body, err = generateSuccessResponse(&newUsers, "users retrieved successfully")
body, err = helper.GenerateSuccessResponse(&newUsers, "users retrieved successfully")
if err != nil {
return nil, nil, err
}
Expand Down
66 changes: 66 additions & 0 deletions example/basic/v20230401/requestmigrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package v20230401

import (
"basicexample/helper"
"encoding/json"
"net/http"
"strings"
"time"
)

// Migrations
type ListUserResponseMigration struct{}

func (c *ListUserResponseMigration) Migrate(
body []byte,
h http.Header) ([]byte, http.Header, error) {
type oldUser struct {
UID string `json:"uid"`
Email string `json:"email"`
FullName string `json:"full_name"`
Profile string `json:"profile"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

var res helper.ServerResponse
err := json.Unmarshal(body, &res)
if err != nil {
return nil, nil, err
}

var users []*oldUser20230501
err = json.Unmarshal(res.Data, &users)
if err != nil {
return nil, nil, err
}

var newUsers []*oldUser
for _, u := range users {
var oldUser oldUser
oldUser.UID = u.UID
oldUser.Email = u.Email
oldUser.FullName = strings.Join([]string{u.FirstName, u.LastName}, " ")
oldUser.Profile = u.Profile
oldUser.CreatedAt = u.CreatedAt
oldUser.UpdatedAt = u.UpdatedAt
newUsers = append(newUsers, &oldUser)
}

body, err = helper.GenerateSuccessResponse(&newUsers, "users retrieved successfully")
if err != nil {
return nil, nil, err
}

return body, h, nil
}

type oldUser20230501 struct {
UID string `json:"uid"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Profile string `json:"profile"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Loading

0 comments on commit 35de2a3

Please sign in to comment.