Skip to content

Commit b593a4e

Browse files
author
Jeff Mataya
committed
Build out the HTTP interface
- Use echo as the http server - Wrap echo's context so that we can get real simple routing and error handling
1 parent ecdb965 commit b593a4e

12 files changed

+240
-13
lines changed

remote/controllers/channels.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package controllers
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/FoxComm/highlander/remote/models/phoenix"
7+
"github.com/FoxComm/highlander/remote/payloads"
8+
"github.com/FoxComm/highlander/remote/responses"
9+
)
10+
11+
// GetChannel finds a single channel by its ID.
12+
func GetChannel(id int) ControllerFunc {
13+
return func() *responses.Response {
14+
channel := phoenix.Channel{
15+
ID: 1,
16+
Name: "The Perfect Gourmet",
17+
PurchaseLocation: 1,
18+
}
19+
20+
return &responses.Response{
21+
StatusCode: http.StatusOK,
22+
Body: channel,
23+
}
24+
}
25+
}
26+
27+
// CreateChannel creates a new channel.
28+
func CreateChannel(payload *payloads.CreateChannel) ControllerFunc {
29+
return func() *responses.Response {
30+
return &responses.Response{
31+
StatusCode: http.StatusCreated,
32+
Body: "I'm a channel",
33+
}
34+
}
35+
}

remote/controllers/controller_func.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package controllers
2+
3+
import "github.com/FoxComm/highlander/remote/responses"
4+
5+
// ControllerFunc is the execution API that any function called in a route must
6+
// conform to. It executes, then returns a response that can be handled.
7+
type ControllerFunc func() *responses.Response

remote/controllers/controllers.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package controllers
2+
3+
import (
4+
"github.com/labstack/echo"
5+
)
6+
7+
// Start initializes all the controllers and routes.
8+
func Start() {
9+
e := echo.New()
10+
11+
e.Use(func(h echo.HandlerFunc) echo.HandlerFunc {
12+
return func(c echo.Context) error {
13+
fc := NewFoxContext(c)
14+
return h(fc)
15+
}
16+
})
17+
18+
e.GET("/v1/public/channels/:id", func(c echo.Context) error {
19+
fc := c.(*FoxContext)
20+
id := fc.ParamInt("id")
21+
return fc.Run(GetChannel(id))
22+
})
23+
24+
e.Start(":9898")
25+
}

remote/controllers/errors.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package controllers
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/FoxComm/highlander/remote/responses"
8+
)
9+
10+
func errParamMustBeNumber(paramName string) *responses.Response {
11+
return &responses.Response{
12+
StatusCode: http.StatusBadRequest,
13+
Errs: []error{
14+
fmt.Errorf("Param %s must be a number", paramName),
15+
},
16+
}
17+
}
18+
19+
func errParamNotFound(paramName string) *responses.Response {
20+
return &responses.Response{
21+
StatusCode: http.StatusBadRequest,
22+
Errs: []error{
23+
fmt.Errorf("Param %s not found", paramName),
24+
},
25+
}
26+
}

remote/controllers/fox_context.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package controllers
2+
3+
import (
4+
"strconv"
5+
6+
"github.com/FoxComm/highlander/remote/responses"
7+
"github.com/labstack/echo"
8+
)
9+
10+
// FoxContext is a wrapper around echo.Context that eases error handling,
11+
// provides helper methods, and ensures we have consistent response handling.
12+
type FoxContext struct {
13+
echo.Context
14+
resp *responses.Response
15+
}
16+
17+
// NewFoxContext creates a new FoxContext from an existing echo.Context.
18+
func NewFoxContext(c echo.Context) *FoxContext {
19+
return &FoxContext{c, nil}
20+
}
21+
22+
// ParamInt parses an integer from the parameters list (as defined by the URI).
23+
func (fc *FoxContext) ParamInt(name string) int {
24+
if fc.resp != nil {
25+
return 0
26+
}
27+
28+
param := fc.Param(name)
29+
if param == "" {
30+
fc.resp = errParamNotFound(name)
31+
return 0
32+
}
33+
34+
paramInt, err := strconv.Atoi(param)
35+
if err != nil {
36+
fc.resp = errParamMustBeNumber(name)
37+
return 0
38+
}
39+
40+
return paramInt
41+
}
42+
43+
// ParamString parses an string from the parameters list (as defined by the URI).
44+
func (fc *FoxContext) ParamString(name string) string {
45+
if fc.resp != nil {
46+
return ""
47+
}
48+
49+
param := fc.Param(name)
50+
if param == "" {
51+
fc.resp = errParamNotFound(name)
52+
return ""
53+
}
54+
55+
return param
56+
}
57+
58+
// Run executes the primary controller method and returns the response.
59+
func (fc *FoxContext) Run(ctrlFn ControllerFunc) error {
60+
if fc.resp != nil {
61+
return fc.handleResponse(fc.resp)
62+
}
63+
64+
return fc.handleResponse(ctrlFn())
65+
}
66+
67+
func (fc *FoxContext) handleResponse(resp *responses.Response) error {
68+
if len(resp.Errs) == 0 {
69+
return fc.JSON(resp.StatusCode, resp.Body)
70+
}
71+
72+
errors := make([]string, len(resp.Errs))
73+
for i, err := range resp.Errs {
74+
errors[i] = err.Error()
75+
}
76+
77+
errResp := map[string][]string{
78+
"errors": errors,
79+
}
80+
81+
return fc.JSON(resp.StatusCode, errResp)
82+
}

remote/glide.lock

+28-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

remote/glide.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
package: github.com/FoxComm/highlander/remote
22
import:
3-
- package: github.com/gin-gonic/gin
3+
- package: github.com/labstack/echo
4+
- package: github.com/lib/pq

remote/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package main
22

3-
import "fmt"
3+
import "github.com/FoxComm/highlander/remote/controllers"
44

55
func main() {
6-
fmt.Println("Hello, I'm remote")
6+
controllers.Start()
77
}

remote/models/phoenix/channel.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import "time"
55
// Channel represents an avenue for purchasing on the Fox Platform. This could
66
// be a website (theperfectgourmet.com), third-party (Amazon), or sale type (B2B).
77
type Channel struct {
8-
ID int64
8+
ID int
99
Name string
10-
PurchaseLocation PurchaseLocation
10+
PurchaseLocation int
1111
CreatedAt time.Time
1212
UpdatedAt time.Time
1313
}

remote/models/phoenix/purchase_location.go

-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@ const (
77
// PurchaseOffFox means the channel's transactions happen off the platform.
88
PurchaseOffFox
99
)
10-
11-
// PurchaseLocation signifies whether a channel's transactions occur on the
12-
// platform, or whether they occur in a different location.
13-
type PurchaseLocation int64

remote/payloads/channel_payloads.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package payloads
2+
3+
import "github.com/FoxComm/highlander/remote/models/phoenix"
4+
5+
// CreateChannel is the structure of payload needed to create a channel.
6+
type CreateChannel struct {
7+
Name string `json:"name"`
8+
PurchaseOnFox bool `json:"purchaseOnFox"`
9+
CatalogID *int64 `json:"catalogId"`
10+
}
11+
12+
// PhoenixModel returns the phoenix model for this payload.
13+
func (c CreateChannel) PhoenixModel() *phoenix.Channel {
14+
model := &phoenix.Channel{Name: c.Name}
15+
16+
if c.PurchaseOnFox {
17+
model.PurchaseLocation = phoenix.PurchaseOnFox
18+
} else {
19+
model.PurchaseLocation = phoenix.PurchaseOffFox
20+
}
21+
22+
return model
23+
}

remote/responses/response.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package responses
2+
3+
// Response is a generic HTTP response type.
4+
type Response struct {
5+
StatusCode int
6+
Body interface{}
7+
Errs []error
8+
}

0 commit comments

Comments
 (0)