Skip to content

Commit 3a3cd16

Browse files
committed
Updated API implementation
All endpoints (search, list, engines) are implemented using different endpoint. A middleware is added to add default arguments (engine) when its not passed. Closes #10
1 parent 63e8eee commit 3a3cd16

File tree

8 files changed

+140
-39
lines changed

8 files changed

+140
-39
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Or download from Github [Releases](https://github.com/bisoncorps/gophie/releases
3030

3131
## Usage
3232

33+
### CLI
34+
3335
gophie
3436

3537
```bash
@@ -61,6 +63,16 @@ Gophie - Bisoncorp (2020) (https://github.com/bisoncorps/gophie)
6163

6264
For Development use `go run main.go [command]`
6365

66+
### API
67+
68+
The available api endpoints with the possible params are
69+
- Search `GET /search?query=query`
70+
- List `GET /list?page=pageNumber`
71+
- Engine `GET /engines`
72+
73+
All endpoints have the following extra params that can be passed
74+
- `engine=engine`: defaults to `netnaija`
75+
6476
### Supported Engines
6577

6678
- NetNaija

cmd/api.go

+86-32
Original file line numberDiff line numberDiff line change
@@ -27,54 +27,105 @@ import (
2727

2828
var port string
2929

30-
// Handler : handles serving gophie
31-
func Handler(w http.ResponseWriter, r *http.Request) {
32-
search := r.URL.Query().Get("search")
33-
list := r.URL.Query().Get("list")
30+
func enableCors(w *http.ResponseWriter) {
31+
(*w).Header().Set("Access-Control-Allow-Origin", "*")
32+
}
33+
34+
func getDefaultsMiddleware(handler http.HandlerFunc) http.HandlerFunc {
35+
return func(w http.ResponseWriter, r *http.Request) {
36+
enableCors(&w)
37+
w.Header().Add("Content-Type", "application/json")
38+
39+
// Set Default Engine to NetNaija
40+
engine := r.URL.Query().Get("engine")
41+
if engine == "" {
42+
q := r.URL.Query()
43+
q.Add("engine", "netnaija")
44+
r.URL.RawQuery = q.Encode()
45+
}
46+
handler.ServeHTTP(w, r)
47+
}
48+
}
49+
50+
// ListHandler : handles List Requests
51+
func ListHandler(w http.ResponseWriter, r *http.Request) {
3452
eng := r.URL.Query().Get("engine")
53+
site, err := engine.GetEngine(eng)
54+
if site == nil {
55+
http.Error(w, "Invalid Engine Param", http.StatusBadRequest)
56+
}
57+
pageNum, err := strconv.Atoi(r.URL.Query().Get("page"))
58+
if err != nil {
59+
http.Error(w, "Page must be a number", http.StatusBadRequest)
60+
}
61+
62+
log.Debug("listing page ", pageNum)
63+
result := site.List(pageNum)
64+
b, err := json.Marshal(result.Movies)
65+
if err != nil {
66+
log.Fatal("failed to serialize response: ", err)
67+
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
68+
}
69+
w.Header().Add("Content-Type", "application/json")
70+
w.Write(b)
71+
log.Debug("Completed query for ", pageNum)
72+
}
73+
74+
// SearchHandler : handles search requests
75+
func SearchHandler(w http.ResponseWriter, r *http.Request) {
3576
var (
3677
result engine.SearchResult
3778
site engine.Engine
3879
)
39-
if search == "" && list == "" {
40-
log.Debug("missing search and list argument")
41-
http.Error(w, "search and list argument is missing in url", http.StatusForbidden)
42-
return
43-
}
44-
if eng == "" {
45-
// Use NetNaija as the default engine
46-
site = engine.NewNetNaijaEngine()
47-
} else {
48-
site = engine.GetEngine(eng)
80+
query := r.URL.Query().Get("query")
81+
if query == "" {
82+
http.Error(w, "Query param must be added to url", http.StatusBadRequest)
4983
}
5084

51-
log.Debug("Using Engine ", site)
52-
if search != "" {
53-
log.Debug("Searching for ", search)
54-
result = site.Search(search)
55-
} else if list != "" {
56-
log.Debug("listing page ", list)
57-
pagenum, err := strconv.Atoi(list)
58-
if err != nil {
59-
http.Error(w, "Page must be a number", http.StatusBadRequest)
60-
}
61-
result = site.List(pagenum)
85+
eng := r.URL.Query().Get("engine")
86+
site, err := engine.GetEngine(eng)
87+
if err != nil {
88+
http.Error(w, "Invalid Engine Param", http.StatusBadRequest)
6289
}
90+
log.Debug("Using Engine ", site)
91+
log.Debug("Searching for ", query)
92+
result = site.Search(query)
6393

6494
// dump results
6595
b, err := json.Marshal(result.Movies)
6696
if err != nil {
67-
log.Fatal("failed to serialize response: ", err)
97+
log.Error("failed to serialize response: ", err)
6898
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
6999
}
70-
enableCors(&w)
71-
w.Header().Add("Content-Type", "application/json")
72100
w.Write(b)
73-
log.Debug("Completed search for ", search, list)
101+
log.Debug("Completed search for ", query)
74102
}
75103

76-
func enableCors(w *http.ResponseWriter) {
77-
(*w).Header().Set("Access-Control-Allow-Origin", "*")
104+
// EngineHandler : handles Engine Listing
105+
func EngineHandler(w http.ResponseWriter, r *http.Request) {
106+
eng := r.URL.Query().Get("engine")
107+
var (
108+
response []byte
109+
err error
110+
)
111+
if eng != "" {
112+
site, err := engine.GetEngine(eng)
113+
if err != nil {
114+
http.Error(w, "Invalid Engine Param", http.StatusBadRequest)
115+
}
116+
response, err = json.Marshal(site)
117+
if err != nil {
118+
log.Error("failed to serialize response: ", err)
119+
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
120+
}
121+
} else {
122+
response, err = json.Marshal(engine.GetEngines())
123+
if err != nil {
124+
log.Error("failed to serialize response: ", err)
125+
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
126+
}
127+
}
128+
w.Write(response)
78129
}
79130

80131
// apiCmd represents the api command
@@ -83,7 +134,10 @@ var apiCmd = &cobra.Command{
83134
Short: "host gophie as an API on a PORT env variable, fallback to set argument",
84135
Long: ``,
85136
Run: func(cmd *cobra.Command, args []string) {
86-
http.HandleFunc("/", Handler)
137+
http.HandleFunc("/search", getDefaultsMiddleware(SearchHandler))
138+
http.HandleFunc("/list", getDefaultsMiddleware(ListHandler))
139+
http.HandleFunc("/engine", EngineHandler)
140+
87141
log.Info("listening on ", port)
88142
_, err := strconv.Atoi(port)
89143
if err != nil {

cmd/api_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func TestAPI(t *testing.T) {
1010
ts := httptest.NewServer(http.HandlerFunc(Handler))
1111
defer ts.Close()
1212

13-
res, _ := http.Get(ts.URL + "/?search=good+boys")
13+
res, _ := http.Get(ts.URL + "/search?query=good+boys")
1414
if res.StatusCode != 200 {
1515
t.Errorf("Server failing")
1616
}

cmd/engines.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121

2222
"github.com/bisoncorps/gophie/engine"
23+
log "github.com/sirupsen/logrus"
2324
"github.com/spf13/cobra"
2425
)
2526

@@ -60,7 +61,11 @@ var showEngineCmd = &cobra.Command{
6061
Short: "Show summary of engine ",
6162
Args: cobra.ExactArgs(1),
6263
Run: func(cmd *cobra.Command, args []string) {
63-
b, err := json.MarshalIndent(engine.GetEngine(args[0]), "", " ")
64+
e, err := engine.GetEngine(args[0])
65+
if err != nil {
66+
log.Fatal(err)
67+
}
68+
b, err := json.MarshalIndent(e, "", " ")
6469
if err != nil {
6570
fmt.Println("error:", err)
6671
}

cmd/list.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import (
2727
var pageNum int
2828

2929
func listPager(cmd *cobra.Command, pageNum int) {
30-
selectedEngine := engine.GetEngine(viper.GetString("engine"))
30+
selectedEngine, err := engine.GetEngine(viper.GetString("engine"))
31+
if err != nil {
32+
log.Fatal(err)
33+
}
3134
var result engine.SearchResult
3235
var items []string
3336
// Initialize process and show loader on terminal and store result in result

cmd/search.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ var searchCmd = &cobra.Command{
3838
Args: cobra.MinimumNArgs(1),
3939
Run: func(cmd *cobra.Command, args []string) {
4040
// Engine is set from root.go
41-
selectedEngine := engine.GetEngine(viper.GetString("engine"))
41+
selectedEngine, err := engine.GetEngine(viper.GetString("engine"))
42+
if err != nil {
43+
log.Fatal(err)
44+
}
4245
query := strings.Join(args, " ")
4346
var result engine.SearchResult
4447
// Initialize process and show loader on terminal and store result in result

engine/engine_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
func TestNetNaija(t *testing.T) {
1010
counter := map[string]int{}
11-
scrapehandler := GetEngine("NetNaija")
11+
scrapehandler, _ := GetEngine("NetNaija")
1212
result := scrapehandler.Search("avenge")
1313

1414
if len(result.Movies) < 1 {

engine/engines.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ type Props struct {
2121
Description string
2222
}
2323

24+
// PropsJSON : JSON structure of all downloadable movies
25+
type PropsJSON struct {
26+
Props
27+
BaseURL string
28+
SearchURL string
29+
ListURL string
30+
}
31+
32+
// MarshalJSON Props structure to return from api
33+
func (p *Props) MarshalJSON() ([]byte, error) {
34+
props := PropsJSON{
35+
Props: *p,
36+
BaseURL: p.BaseURL.String(),
37+
SearchURL: p.SearchURL.String(),
38+
ListURL: p.ListURL.String(),
39+
}
40+
41+
return json.Marshal(props)
42+
}
43+
2444
// Engine : interface for all engines
2545
type Engine interface {
2646
Search(query string) SearchResult
@@ -116,8 +136,12 @@ func GetEngines() map[string]Engine {
116136
}
117137

118138
// GetEngine : Return an engine
119-
func GetEngine(engine string) Engine {
120-
return GetEngines()[strings.ToLower(engine)]
139+
func GetEngine(engine string) (Engine, error) {
140+
e := GetEngines()[strings.ToLower(engine)]
141+
if e == nil {
142+
return nil, fmt.Errorf("Engine %s Does not exist", engine)
143+
}
144+
return e, nil
121145
}
122146

123147
// Get the movie index context stored in Request

0 commit comments

Comments
 (0)