Skip to content

Commit c526aaa

Browse files
authored
Merge pull request #10 from subomi/subomi/feat/improve-example
feat: improved examples and fixed migration bug
2 parents 9bd2106 + d696ac2 commit c526aaa

File tree

5 files changed

+170
-36
lines changed

5 files changed

+170
-36
lines changed

example/basic/call_api.sh

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,35 @@
22

33
helpFunc() {
44
echo "Usage: $0 -r"
5+
echo -e "\t-n Specify how many iterations we call the API."
56
echo -e "\t-r Specify request type, see options below:"
67
echo -e "\t - lu: list users without versioning"
78
echo -e "\t - lvu: list users with versioning"
89
exit 1
910
}
1011

11-
while getopts ":r:" opt; do
12+
n=1
13+
14+
while getopts ":n:r:" opt; do
1215
case "$opt" in
16+
n)
17+
n=$OPTARG
18+
;;
1319
r)
1420
req="$OPTARG"
1521

1622
if [[ "$req" == "lu" ]]; then
17-
curl -s localhost:9000/users \
18-
-H "Content-Type: application/json" | jq
23+
for ((i=0; i<n; i++)); do
24+
curl -s localhost:9000/users \
25+
-H "Content-Type: application/json" | jq
26+
done
1927

2028
elif [[ "$req" == "lvu" ]]; then
21-
curl -s localhost:9000/users \
22-
-H "Content-Type: application/json" \
23-
-H "X-Example-Version: 2023-09-02" | jq
29+
for ((i=0; i<n; i++)); do
30+
curl -s localhost:9000/users \
31+
-H "Content-Type: application/json" \
32+
-H "X-Example-Version: 2023-04-01" | jq
33+
done
2434

2535
else
2636
helpFunc

example/basic/main.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package main
33
import (
44
"encoding/json"
55
"log"
6+
"math/rand"
67
"net/http"
78
"os"
89
"os/signal"
10+
"time"
911

1012
"github.com/gorilla/mux"
1113
"github.com/prometheus/client_golang/prometheus"
@@ -14,16 +16,26 @@ import (
1416
)
1517

1618
func main() {
17-
rm := rms.NewRequestMigration(
19+
// Set the seed value for the random number generator
20+
rand.Seed(time.Now().UnixNano())
21+
22+
rm, err := rms.NewRequestMigration(
1823
&rms.RequestMigrationOptions{
1924
VersionHeader: "X-Example-Version",
20-
CurrentVersion: "2023-09-02",
25+
CurrentVersion: "2023-05-01",
2126
VersionFormat: rms.DateFormat,
2227
})
2328

29+
if err != nil {
30+
log.Fatal(err)
31+
}
32+
2433
rm.RegisterMigrations(rms.Migrations{
25-
"2023-09-02": []rms.Migration{
26-
&CombineNamesForUserMigration{},
34+
"2023-05-01": []rms.Migration{
35+
&expandProfileForUserMigration{},
36+
},
37+
"2023-04-01": []rms.Migration{
38+
&combineNamesForUserMigration{},
2739
},
2840
})
2941

@@ -33,7 +45,7 @@ func main() {
3345
Handler: buildMux(api),
3446
}
3547

36-
err := backend.ListenAndServe()
48+
err = backend.ListenAndServe()
3749
if err != nil {
3850
log.Fatal(err)
3951
}
@@ -45,9 +57,9 @@ func main() {
4557

4658
func buildMux(api *API) http.Handler {
4759
m := mux.NewRouter()
48-
m.Use(api.rm.VersionAPI)
4960

50-
m.HandleFunc("/users", api.ListUser).Methods("GET")
61+
m.Handle("/users",
62+
api.rm.VersionAPI(http.HandlerFunc(api.ListUser))).Methods("GET")
5163
m.HandleFunc("/users/{id}", api.GetUser).Methods("GET")
5264

5365
reg := prometheus.NewRegistry()
@@ -73,6 +85,10 @@ type API struct {
7385
}
7486

7587
func (a *API) ListUser(w http.ResponseWriter, r *http.Request) {
88+
// Generate a random Int type number between 1 and 10
89+
randNum := rand.Intn(3-1+1) + 1
90+
time.Sleep(time.Duration(randNum) * time.Second)
91+
7692
users, err := a.store.GetAll()
7793
if err != nil {
7894
w.WriteHeader(http.StatusBadGateway)

example/basic/requestmigrations.go

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,98 @@ import (
1010
"time"
1111
)
1212

13-
type OldUser struct {
13+
// Migrations
14+
type combineNamesForUserMigration struct{}
15+
16+
func (c *combineNamesForUserMigration) ShouldMigrateRequest(r *http.Request) bool {
17+
return false
18+
}
19+
20+
func (c *combineNamesForUserMigration) MigrateRequest(r *http.Request) error {
21+
fmt.Println("migrating request...")
22+
23+
return nil
24+
}
25+
26+
func (c *combineNamesForUserMigration) ShouldMigrateResponse(
27+
req *http.Request,
28+
res *http.Response) bool {
29+
30+
isUserPath := req.URL.Path == "/users"
31+
isGetMethod := req.Method == http.MethodGet
32+
33+
return isUserPath && isGetMethod
34+
}
35+
36+
func (c *combineNamesForUserMigration) MigrateResponse(r *http.Response) error {
37+
type oldUser struct {
38+
UID string `json:"uid"`
39+
Email string `json:"email"`
40+
FullName string `json:"full_name"`
41+
Profile string `json:"profile"`
42+
CreatedAt time.Time `json:"created_at"`
43+
UpdatedAt time.Time `json:"updated_at"`
44+
}
45+
46+
body, err := io.ReadAll(r.Body)
47+
if err != nil {
48+
return err
49+
}
50+
51+
var res ServerResponse
52+
err = json.Unmarshal(body, &res)
53+
if err != nil {
54+
return err
55+
}
56+
57+
var users []*oldUser20230501
58+
err = json.Unmarshal(res.Data, &users)
59+
if err != nil {
60+
return err
61+
}
62+
63+
var newUsers []*oldUser
64+
for _, u := range users {
65+
var oldUser oldUser
66+
oldUser.UID = u.UID
67+
oldUser.Email = u.Email
68+
oldUser.FullName = strings.Join([]string{u.FirstName, u.LastName}, " ")
69+
oldUser.Profile = u.Profile
70+
oldUser.CreatedAt = u.CreatedAt
71+
oldUser.UpdatedAt = u.UpdatedAt
72+
newUsers = append(newUsers, &oldUser)
73+
}
74+
75+
body, err = generateSuccessResponse(&newUsers, "users retrieved successfully")
76+
if err != nil {
77+
return err
78+
}
79+
80+
r.Body = io.NopCloser(bytes.NewReader(body))
81+
return nil
82+
}
83+
84+
type oldUser20230501 struct {
1485
UID string `json:"uid"`
1586
Email string `json:"email"`
16-
Name string `json:"name"`
87+
FirstName string `json:"first_name"`
88+
LastName string `json:"last_name"`
89+
Profile string `json:"profile"`
1790
CreatedAt time.Time `json:"created_at"`
1891
UpdatedAt time.Time `json:"updated_at"`
1992
}
2093

21-
// Migrations
22-
type CombineNamesForUserMigration struct{}
94+
type expandProfileForUserMigration struct{}
2395

24-
func (c *CombineNamesForUserMigration) ShouldMigrateRequest(r *http.Request) bool {
25-
return true
96+
func (e *expandProfileForUserMigration) ShouldMigrateRequest(r *http.Request) bool {
97+
return false
2698
}
2799

28-
func (c *CombineNamesForUserMigration) MigrateRequest(r *http.Request) error {
29-
fmt.Println("migrating request...")
30-
100+
func (e *expandProfileForUserMigration) MigrateRequest(r *http.Request) error {
31101
return nil
32102
}
33103

34-
func (c *CombineNamesForUserMigration) ShouldMigrateResponse(
104+
func (e *expandProfileForUserMigration) ShouldMigrateResponse(
35105
req *http.Request,
36106
res *http.Response) bool {
37107

@@ -41,7 +111,7 @@ func (c *CombineNamesForUserMigration) ShouldMigrateResponse(
41111
return isUserPath && isGetMethod
42112
}
43113

44-
func (c *CombineNamesForUserMigration) MigrateResponse(r *http.Response) error {
114+
func (e *expandProfileForUserMigration) MigrateResponse(r *http.Response) error {
45115
body, err := io.ReadAll(r.Body)
46116
if err != nil {
47117
return err
@@ -59,18 +129,20 @@ func (c *CombineNamesForUserMigration) MigrateResponse(r *http.Response) error {
59129
return err
60130
}
61131

62-
var newUsers []*OldUser
132+
var newUsers []*oldUser20230501
63133
for _, u := range users {
64-
var oldUser OldUser
134+
var oldUser oldUser20230501
65135
oldUser.UID = u.UID
66136
oldUser.Email = u.Email
67-
oldUser.Name = strings.Join([]string{u.FirstName, u.LastName}, " ")
137+
oldUser.FirstName = u.FirstName
138+
oldUser.LastName = u.LastName
139+
oldUser.Profile = u.Profile.UID
68140
oldUser.CreatedAt = u.CreatedAt
69141
oldUser.UpdatedAt = u.UpdatedAt
70142
newUsers = append(newUsers, &oldUser)
71143
}
72144

73-
body, err = json.Marshal(&newUsers)
145+
body, err = generateSuccessResponse(&newUsers, "users retrieved successfully")
74146
if err != nil {
75147
return err
76148
}

example/basic/store.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ func init() {
1616
1717
FirstName: "Subomi",
1818
LastName: "Oluwalana",
19+
Profile: &profile{
20+
UID: "999",
21+
GithubURL: "https://github.com/subomi",
22+
TwitterURL: "https://twitter.com/subomiOluwalana",
23+
},
1924
CreatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
2025
UpdatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
2126
},
@@ -24,6 +29,11 @@ func init() {
2429
2530
FirstName: "Raymond",
2631
LastName: "Tukpe",
32+
Profile: &profile{
33+
UID: "888",
34+
GithubURL: "https://github.com/jirevwe",
35+
TwitterURL: "https://twitter.com/rtukpe",
36+
},
2737
CreatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
2838
UpdatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
2939
},
@@ -32,6 +42,11 @@ func init() {
3242
3343
FirstName: "Daniel",
3444
LastName: "Oluojomu",
45+
Profile: &profile{
46+
UID: "777",
47+
GithubURL: "https://github.com/danvixent",
48+
TwitterURL: "https://twitter.com/danvixent",
49+
},
3550
CreatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
3651
UpdatedAt: time.Date(2023, time.March, 10, 7, 0, 0, 0, time.UTC),
3752
},
@@ -45,10 +60,17 @@ type User struct {
4560
Email string `json:"email"`
4661
FirstName string `json:"first_name"`
4762
LastName string `json:"last_name"`
63+
Profile *profile `json:"profile"`
4864
CreatedAt time.Time `json:"created_at"`
4965
UpdatedAt time.Time `json:"updated_at"`
5066
}
5167

68+
type profile struct {
69+
UID string `json:"uid"`
70+
GithubURL string `json:"github_url"`
71+
TwitterURL string `json:"twitter_url"`
72+
}
73+
5274
type Store struct {
5375
mu sync.Mutex
5476
users []*User

requestmigrations.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package requestmigrations
22

33
import (
44
"errors"
5+
"fmt"
56
"io"
67
"net/http"
78
"net/http/httptest"
@@ -25,14 +26,16 @@ var (
2526
ErrInvalidVersionFormat = errors.New("Invalid version format")
2627
)
2728

28-
// migrations := Migrations{
29-
// "2023-02-28": []Migration{
30-
// Migration{},
31-
// Migration{},
32-
// },
33-
// }
29+
// migrations := Migrations{
30+
// "2023-02-28": []Migration{
31+
// Migration{},
32+
// Migration{},
33+
// },
34+
// }
3435
type Migrations map[string][]Migration
3536

37+
// Migration is the core interface each transformation in every version
38+
// needs to implement.
3639
type Migration interface {
3740
ShouldMigrateRequest(req *http.Request) bool
3841
MigrateRequest(req *http.Request) error
@@ -129,6 +132,7 @@ func (rm *RequestMigration) VersionAPI(next http.Handler) http.Handler {
129132
return
130133
}
131134

135+
fmt.Printf("from: %v, to: %v\n", from, to)
132136
if from.Equal(to) {
133137
next.ServeHTTP(w, req)
134138
return
@@ -240,6 +244,11 @@ func (m *Migrator) applyRequestMigrations(req *http.Request) error {
240244
return ErrInvalidVersion
241245
}
242246

247+
// skip initial version.
248+
if m.from.Equal(version) {
249+
continue
250+
}
251+
243252
for _, migration := range migrations {
244253
if !migration.ShouldMigrateRequest(req) {
245254
continue
@@ -261,12 +270,17 @@ func (m *Migrator) applyResponseMigrations(
261270
res := rr.Result()
262271

263272
for i := len(m.versions); i > 0; i-- {
264-
v := m.versions[i-1].String()
265-
migrations, ok := m.migrations[v]
273+
version := m.versions[i-1]
274+
migrations, ok := m.migrations[version.String()]
266275
if !ok {
267276
return ErrServerError
268277
}
269278

279+
// skip initial version.
280+
if m.from.Equal(version) {
281+
continue
282+
}
283+
270284
for _, migration := range migrations {
271285
if !migration.ShouldMigrateResponse(req, res) {
272286
continue

0 commit comments

Comments
 (0)