diff --git a/README.md b/README.md index da8cfd2..631a97e 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,9 @@ There's also a 1% chance for the server to claim to be a [Teapot](https://tools. **Note**: JSON keys must be strings, providing your response codes as integers will not work! +### Registering endpoints automatically +The service will scan the `endpoints/` directory relative to the currenct working directory on startup. You can change the directory to be scanned by adding a flag `--endpoint-dir=ENDPOINT_DIR`. + # Tags Apidemic uses tags to annotate what kind of fake data to generate and also control different requrements of fake data. diff --git a/api.go b/api.go index e6c1168..5cee6c3 100644 --- a/api.go +++ b/api.go @@ -13,7 +13,7 @@ import ( ) // Version is the version of apidemic. Apidemic uses semver. -const Version = "0.4" +const Version = "0.5" var maxItemTime = cache.DefaultExpiration @@ -149,6 +149,7 @@ func DynamicEndpoint(w http.ResponseWriter, r *http.Request) { return } } + responseText := fmt.Sprintf("apidemic: %s has no %s endpoint", vars["endpoint"], r.Method) RenderJSON(w, http.StatusNotFound, NewResponse(responseText)) } diff --git a/api_test.go b/api_test.go index e6b198a..37264a5 100644 --- a/api_test.go +++ b/api_test.go @@ -3,7 +3,6 @@ package apidemic import ( "bytes" "encoding/json" - "io" "io/ioutil" "net/http" "net/http/httptest" @@ -21,7 +20,7 @@ func TestDynamicEndpointFailsWithoutRegistration(t *testing.T) { payload := registerPayload(t, "fixtures/sample_request.json") w := httptest.NewRecorder() - req := jsonRequest("POST", "/api/test", payload) + req := JsonRequest("POST", "/api/test", payload) s.ServeHTTP(w, req) assert.Equal(t, http.StatusNotFound, w.Code) @@ -32,12 +31,12 @@ func TestDynamicEndpointWithGetRequest(t *testing.T) { payload := registerPayload(t, "fixtures/sample_request.json") w := httptest.NewRecorder() - req := jsonRequest("POST", "/register", payload) + req := JsonRequest("POST", "/register", payload) s.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) w = httptest.NewRecorder() - req = jsonRequest("GET", "/api/test", nil) + req = JsonRequest("GET", "/api/test", nil) s.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) @@ -49,12 +48,12 @@ func TestDynamicEndpointWithPostRequest(t *testing.T) { payload["http_method"] = "POST" w := httptest.NewRecorder() - req := jsonRequest("POST", "/register", payload) + req := JsonRequest("POST", "/register", payload) s.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) w = httptest.NewRecorder() - req = jsonRequest("POST", "/api/test", nil) + req = JsonRequest("POST", "/api/test", nil) s.ServeHTTP(w, req) assert.Equal(t, http.StatusCreated, w.Code) @@ -66,12 +65,12 @@ func TestDynamicEndpointWithForbiddenResponse(t *testing.T) { registerPayload["response_code_probabilities"] = map[string]int{"403": 100} w := httptest.NewRecorder() - req := jsonRequest("POST", "/register", registerPayload) + req := JsonRequest("POST", "/register", registerPayload) s.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) w = httptest.NewRecorder() - req = jsonRequest("GET", "/api/test", nil) + req = JsonRequest("GET", "/api/test", nil) s.ServeHTTP(w, req) assert.Equal(t, http.StatusForbidden, w.Code) @@ -97,20 +96,3 @@ func registerPayload(t *testing.T, fixtureFile string) map[string]interface{} { return api } - -func jsonRequest(method string, path string, body interface{}) *http.Request { - var bEnd io.Reader - if body != nil { - b, err := json.Marshal(body) - if err != nil { - return nil - } - bEnd = bytes.NewReader(b) - } - req, err := http.NewRequest(method, path, bEnd) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", "application/json") - return req -} diff --git a/cmd/apidemic/main.go b/cmd/apidemic/main.go index bd0b3f5..6d810c1 100644 --- a/cmd/apidemic/main.go +++ b/cmd/apidemic/main.go @@ -1,18 +1,36 @@ package main import ( + "bytes" + "encoding/json" "fmt" + "io/ioutil" "log" "net/http" + "net/http/httptest" + "os" + "path/filepath" "github.com/codegangsta/cli" "github.com/gernest/apidemic" + "github.com/gorilla/mux" ) func server(ctx *cli.Context) { port := ctx.Int("port") + endpointDir := ctx.String("endpoint-dir") s := apidemic.NewServer() + _, err := os.Stat(endpointDir) + if err != nil && endpointDir != "endpoints/" { + fmt.Errorf("endpoint not found: %s", endpointDir) + } + + err = addEndpoints(s, endpointDir) + if err != nil { + log.Fatalln(err) + } + log.Println("starting server on port :", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), s)) } @@ -38,8 +56,66 @@ func main() { Value: 3000, EnvVar: "PORT", }, + cli.StringFlag{ + Name: "endpoint-dir", + Usage: "Directory to scan for endpoint", + Value: "endpoints/", + EnvVar: "ENDPOINT_DIR", + }, }, }, } app.RunAndExitOnError() } + +// addEndpoints iterates over a directory and tries to register each file as an endpoint +// if directory does not exist or not readable it will simply return without registration +// failing registration however causes an error response +func addEndpoints(s *mux.Router, endpointDir string) error { + var registerPayload map[string]interface{} + files, err := ioutil.ReadDir(endpointDir) + if err != nil { + return nil + } + + for _, file := range files { + if file.IsDir() { + continue + } + + fullPath := filepath.Join(endpointDir ,file.Name()) + + registerPayload, err = getRegisterPayload(fullPath) + if err != nil { + return err + } + + w := httptest.NewRecorder() + req := apidemic.JsonRequest("POST", "/register", registerPayload) + s.ServeHTTP(w, req) + + log.Printf("%s is registered\n", fullPath) + + if w.Code != http.StatusOK { + return fmt.Errorf("registering %s failed", fullPath) + } + } + + return nil +} + +func getRegisterPayload(endpoint string) (map[string]interface{}, error) { + var api map[string]interface{} + + content, err := ioutil.ReadFile(endpoint) + if err != nil { + return api, err + } + + err = json.NewDecoder(bytes.NewReader(content)).Decode(&api) + if err != nil { + return api, err + } + + return api, nil +} diff --git a/helper.go b/helper.go new file mode 100644 index 0000000..6dd5081 --- /dev/null +++ b/helper.go @@ -0,0 +1,26 @@ +package apidemic + +import ( + "bytes" + "encoding/json" + "io" + "net/http" +) + +// JsonRequest creates an HTTP request +func JsonRequest(method string, path string, body interface{}) *http.Request { + var bEnd io.Reader + if body != nil { + b, err := json.Marshal(body) + if err != nil { + return nil + } + bEnd = bytes.NewReader(b) + } + req, err := http.NewRequest(method, path, bEnd) + if err != nil { + panic(err) + } + req.Header.Set("Content-Type", "application/json") + return req +}