From 1655a9b83a5213a9534819a4e4f3a36b7714d0dc Mon Sep 17 00:00:00 2001 From: breel Date: Sat, 8 Aug 2020 10:57:46 -0600 Subject: [PATCH] files impl --- server/entities.go | 42 +++++++++++++++++ server/files.go | 108 ++++++++++++++++++++++++++++++++++++++++++++ server/response.go | 22 +++++++++ server/rest.go | 19 +------- server/rest_test.go | 80 ++++++++++++++++++++------------ server/users.go | 32 +++++++++++++ 6 files changed, 256 insertions(+), 47 deletions(-) create mode 100644 server/entities.go create mode 100644 server/files.go create mode 100644 server/users.go diff --git a/server/entities.go b/server/entities.go new file mode 100644 index 0000000..d97f930 --- /dev/null +++ b/server/entities.go @@ -0,0 +1,42 @@ +package server + +import ( + "net/http" +) + +func (rest *REST) entities(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPut: + rest.entitiesReplace(w, r) + case http.MethodPatch: + rest.entitiesUpdate(w, r) + case http.MethodPost: + rest.entitiesCreate(w, r) + case http.MethodGet: + rest.entitiesGet(w, r) + case http.MethodDelete: + rest.entitiesDelete(w, r) + default: + http.NotFound(w, r) + } +} + +func (rest *REST) entitiesCreate(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not impl", http.StatusNotImplemented) +} + +func (rest *REST) entitiesDelete(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not impl", http.StatusNotImplemented) +} + +func (rest *REST) entitiesGet(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not impl", http.StatusNotImplemented) +} + +func (rest *REST) entitiesReplace(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not impl", http.StatusNotImplemented) +} + +func (rest *REST) entitiesUpdate(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not impl", http.StatusNotImplemented) +} diff --git a/server/files.go b/server/files.go new file mode 100644 index 0000000..4da636b --- /dev/null +++ b/server/files.go @@ -0,0 +1,108 @@ +package server + +import ( + "io" + "local/dndex/config" + "net/http" + "os" + "path" +) + +func (rest *REST) files(w http.ResponseWriter, r *http.Request) { + if len(r.URL.Path) < 2 { + http.NotFound(w, r) + return + } + switch r.Method { + case http.MethodPut: + rest.filesUpdate(w, r) + case http.MethodPost: + rest.filesCreate(w, r) + case http.MethodGet: + rest.filesGet(w, r) + case http.MethodDelete: + rest.filesDelete(w, r) + default: + http.NotFound(w, r) + } +} + +func (rest *REST) filesCreate(w http.ResponseWriter, r *http.Request) { + localPath := rest.filesPath(r) + if stat, err := os.Stat(localPath); !os.IsNotExist(err) || (stat != nil && stat.IsDir()) { + rest.respConflict(w) + return + } + f, err := os.Open(localPath) + if err != nil { + rest.respError(w, err) + return + } + defer f.Close() + if _, err := io.Copy(f, r.Body); err != nil { + rest.respError(w, err) + } + rest.respOK(w) +} + +func (rest *REST) filesDelete(w http.ResponseWriter, r *http.Request) { + localPath := rest.filesPath(r) + if stat, err := os.Stat(localPath); os.IsNotExist(err) { + rest.respOK(w) + return + } else if err != nil || stat.IsDir() { + rest.respConflict(w) + return + } + if err := os.Remove(localPath); err != nil { + rest.respError(w, err) + } + rest.respOK(w) +} + +func (rest *REST) filesGet(w http.ResponseWriter, r *http.Request) { + localPath := rest.filesPath(r) + if stat, err := os.Stat(localPath); os.IsNotExist(err) { + http.NotFound(w, r) + return + } else if err != nil || stat.IsDir() { + rest.respConflict(w) + return + } + f, err := os.Open(localPath) + if err != nil { + rest.respError(w, err) + return + } + defer f.Close() + if _, err := io.Copy(w, f); err != nil { + rest.respError(w, err) + } +} + +func (rest *REST) filesUpdate(w http.ResponseWriter, r *http.Request) { + localPath := rest.filesPath(r) + if stat, err := os.Stat(localPath); os.IsNotExist(err) { + http.NotFound(w, r) + return + } else if err != nil || stat.IsDir() { + rest.respConflict(w) + return + } + f, err := os.Open(localPath) + if err != nil { + rest.respError(w, err) + return + } + defer f.Close() + if _, err := io.Copy(f, r.Body); err != nil { + rest.respError(w, err) + } + rest.respOK(w) +} + +func (rest *REST) filesPath(r *http.Request) string { + scope := rest.scope(r) + localPath := path.Join(config.New().FileRoot, scope.Namespace, r.URL.Path) + return localPath +} diff --git a/server/response.go b/server/response.go index 895e00b..03d9bf5 100644 --- a/server/response.go +++ b/server/response.go @@ -2,9 +2,31 @@ package server import ( "encoding/json" + "fmt" "net/http" ) +func (rest *REST) respOK(w http.ResponseWriter) { + rest.respMap(w, "ok", true) +} + +func (rest *REST) respConflict(w http.ResponseWriter) { + rest.respMapStatus(w, "error", "collision found", http.StatusConflict) +} + +func (rest *REST) respError(w http.ResponseWriter, err error) { + rest.respMapStatus(w, "error", fmt.Sprint(err), http.StatusInternalServerError) +} + +func (rest *REST) respBadRequest(w http.ResponseWriter, msg string) { + rest.respMapStatus(w, "error", msg, http.StatusBadRequest) +} + +func (rest *REST) respMapStatus(w http.ResponseWriter, key string, value interface{}, status int) { + w.WriteHeader(status) + rest.resp(w, map[string]interface{}{key: value}) +} + func (rest *REST) respMap(w http.ResponseWriter, key string, value interface{}) { rest.resp(w, map[string]interface{}{key: value}) } diff --git a/server/rest.go b/server/rest.go index a154d16..49fe0c8 100644 --- a/server/rest.go +++ b/server/rest.go @@ -6,7 +6,6 @@ import ( "local/dndex/server/auth" "local/dndex/storage" "local/router" - "log" "net/http" ) @@ -33,12 +32,11 @@ func NewREST(g storage.RateLimitedGraph) (*REST, error) { param := router.Wildcard params := router.Wildcard + router.Wildcard - _, _ = param, params paths := map[string]http.HandlerFunc{ fmt.Sprintf("version"): rest.version, fmt.Sprintf("%s/%s", config.New().FilePrefix, params): rest.files, - fmt.Sprintf("users"): rest.users, + fmt.Sprintf("users/%s", param): rest.users, fmt.Sprintf("entities/%s", params): rest.entities, } @@ -59,18 +57,3 @@ func NewREST(g storage.RateLimitedGraph) (*REST, error) { func (rest *REST) scope(r *http.Request) auth.Scope { return auth.GetScope(r) } - -func (rest *REST) files(w http.ResponseWriter, r *http.Request) { - log.Println("files: SCOPE:", rest.scope(r), r.URL.Path) - http.Error(w, "not impl", http.StatusNotImplemented) -} - -func (rest *REST) users(w http.ResponseWriter, r *http.Request) { - log.Println("users: SCOPE:", rest.scope(r), r.URL.Path) - http.Error(w, "not impl", http.StatusNotImplemented) -} - -func (rest *REST) entities(w http.ResponseWriter, r *http.Request) { - log.Println("entities: SCOPE:", rest.scope(r), r.URL.Path) - http.Error(w, "not impl", http.StatusNotImplemented) -} diff --git a/server/rest_test.go b/server/rest_test.go index 1c9818d..b2e3889 100644 --- a/server/rest_test.go +++ b/server/rest_test.go @@ -28,15 +28,12 @@ var ( ) func TestRESTRouter(t *testing.T) { - rest, setAuth, clean := testREST(t) - defer clean() + _, _, clean := testREST(t) + clean() cases := map[string]struct { method string is404 bool }{ - "/version": { - method: http.MethodGet, - }, fmt.Sprintf(`%s`, config.New().FilePrefix): { method: http.MethodGet, is404: true, @@ -45,9 +42,18 @@ func TestRESTRouter(t *testing.T) { method: http.MethodGet, is404: true, }, - fmt.Sprintf(`%s/%s`, config.New().FilePrefix, testFilename): { + fmt.Sprintf(`%s/%s?g`, config.New().FilePrefix, testFilename): { method: http.MethodGet, }, + fmt.Sprintf(`%s/%s?pu`, config.New().FilePrefix, testFilename): { + method: http.MethodPut, + }, + fmt.Sprintf(`%s/%s?po`, config.New().FilePrefix, testFilename): { + method: http.MethodPost, + }, + fmt.Sprintf(`%s/%s?d`, config.New().FilePrefix, testFilename): { + method: http.MethodDelete, + }, fmt.Sprintf(`%s/fake.fake`, config.New().FilePrefix): { method: http.MethodGet, is404: true, @@ -56,19 +62,23 @@ func TestRESTRouter(t *testing.T) { method: http.MethodGet, is404: true, }, - fmt.Sprintf("/users/%s", testNamespace): { + "/users/register": { + method: http.MethodPost, + }, + "/users/login": { method: http.MethodPost, - is404: true, }, "/users/": { - method: http.MethodGet, + method: http.MethodPost, is404: true, }, "/users": { method: http.MethodPost, + is404: true, }, "/users?": { - method: http.MethodGet, + method: http.MethodPost, + is404: true, }, "/entities": { method: http.MethodGet, @@ -124,37 +134,49 @@ func TestRESTRouter(t *testing.T) { fmt.Sprintf("/entities/%s/connections", testEntityID): { method: http.MethodPatch, }, + "/version": { + method: http.MethodGet, + }, + fmt.Sprintf("/entities/%s/connections/uuid/", testEntityID): { + method: http.MethodDelete, + }, fmt.Sprintf("/entities/%s/connections/uuid", testEntityID): { method: http.MethodDelete, }, - fmt.Sprintf("/entities/%s/connections/", testEntityID): { + fmt.Sprintf("/entities/%s/connections/?d", testEntityID): { + method: http.MethodDelete, + }, + fmt.Sprintf("/entities/%s/connections?d", testEntityID): { + method: http.MethodDelete, + }, + fmt.Sprintf("/entities/%s/?d", testEntityID): { + method: http.MethodDelete, + }, + fmt.Sprintf("/entities/%s?d", testEntityID): { + method: http.MethodDelete, + }, + "/entities/?d": { method: http.MethodDelete, is404: true, }, - fmt.Sprintf("/entities/%s/connections", testEntityID): { - method: http.MethodDelete, - }, - fmt.Sprintf("/entities/%s/", testEntityID): { + "/entities?d": { method: http.MethodDelete, is404: true, }, - fmt.Sprintf("/entities/%s", testEntityID): { - method: http.MethodDelete, - }, } for name, d := range cases { c := d path := name - t.Run(name, func(t *testing.T) { - r := httptest.NewRequest(c.method, path, strings.NewReader(``)) - setAuth(r) - w := httptest.NewRecorder() - rest.router.ServeHTTP(w, r) - if (w.Code == http.StatusNotFound) != c.is404 { - t.Fatalf("want 404==%v, got %d: %+v: %s", c.is404, w.Code, c, w.Body.Bytes()) - } - }) + rest, setAuth, clean := testREST(t) + defer clean() + r := httptest.NewRequest(c.method, path, strings.NewReader(``)) + setAuth(r) + w := httptest.NewRecorder() + rest.router.ServeHTTP(w, r) + if (w.Code == http.StatusNotFound) != c.is404 { + t.Errorf("%s: want 404==%v, got %d: %+v: %s", name, c.is404, w.Code, c, w.Body.Bytes()) + } } } @@ -163,14 +185,14 @@ func testREST(t *testing.T) (*REST, func(r *http.Request), func()) { if err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(path.Join(d, testFilename), []byte(testContent), os.ModePerm); err != nil { + os.MkdirAll(path.Join(d, testNamespace), os.ModePerm) + if err := ioutil.WriteFile(path.Join(d, testNamespace, testFilename), []byte(testContent), os.ModePerm); err != nil { t.Fatal(err) } os.Setenv("FILEROOT", d) os.Setenv("DRIVER_TYPE", "map") os.Setenv("AUTH", "true") os.Args = os.Args[:1] - os.Args = os.Args[:1] config.New() rest, err := NewREST(storage.NewRateLimitedGraph("")) if err != nil { diff --git a/server/users.go b/server/users.go new file mode 100644 index 0000000..c5db016 --- /dev/null +++ b/server/users.go @@ -0,0 +1,32 @@ +package server + +import ( + "log" + "net/http" +) + +func (rest *REST) users(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPost: + default: + http.NotFound(w, r) + } + switch r.URL.Path { + case "/register": + rest.usersRegister(w, r) + case "/login": + rest.usersLogin(w, r) + default: + http.NotFound(w, r) + } +} + +func (rest *REST) usersRegister(w http.ResponseWriter, r *http.Request) { + log.Println(r.URL.Path, rest.scope(r)) + http.Error(w, "not impl", http.StatusNotImplemented) +} + +func (rest *REST) usersLogin(w http.ResponseWriter, r *http.Request) { + log.Println(r.URL.Path, rest.scope(r)) + http.Error(w, "not impl", http.StatusNotImplemented) +}