Add feature to hold configs in filesystem 01/33501/4
authorShashank Kumar Shankar <shashank.kumar.shankar@intel.com>
Thu, 1 Mar 2018 01:54:59 +0000 (17:54 -0800)
committerShashank Kumar Shankar <shashank.kumar.shankar@intel.com>
Sat, 3 Mar 2018 00:52:39 +0000 (16:52 -0800)
This patch adds feature to hold config files
on filesystem and adds all unit tests to have
enough coverage for milestones.

Change-Id: Icd6f3dc93e0f419500f82f0a6ccd62e500dfc918
Issue-ID: MUSIC-42
Signed-off-by: Shashank Kumar Shankar <shashank.kumar.shankar@intel.com>
24 files changed:
README.md
mountpath/default/sampleAAIConfig.properties [moved from src/dkv/configurations/sampleAAIConfig.properties with 100% similarity]
mountpath/default/sampleAPPCConfig.properties [moved from src/dkv/configurations/sampleAPPCConfig.properties with 100% similarity]
src/dkv/Gopkg.lock
src/dkv/api/backendConsulConnection.go [moved from src/dkv/api/consulConnection.go with 96% similarity]
src/dkv/api/backendFilesystemConnection.go [new file with mode: 0644]
src/dkv/api/backendPropertiesConnection.go [new file with mode: 0644]
src/dkv/api/backendfakes.go [new file with mode: 0644]
src/dkv/api/configHandlers.go [new file with mode: 0644]
src/dkv/api/configHandlers_test.go [new file with mode: 0644]
src/dkv/api/consulConnection_test.go [deleted file]
src/dkv/api/endpointViews_fake.go [deleted file]
src/dkv/api/endpointViews_test.go [deleted file]
src/dkv/api/initialise.go
src/dkv/api/propertiesReader.go [deleted file]
src/dkv/api/propertiesReader_test.go [deleted file]
src/dkv/api/queryConsulHandlers.go [moved from src/dkv/api/endpointViews.go with 56% similarity]
src/dkv/api/queryConsulHandlers_test.go [new file with mode: 0644]
src/dkv/api/registrationHandlers.go [new file with mode: 0644]
src/dkv/api/registrationHandlers_test.go [new file with mode: 0644]
src/dkv/api/token_service_map.json [new file with mode: 0644]
src/dkv/api/utils.go [new file with mode: 0644]
src/dkv/api/utils_test.go [new file with mode: 0644]
src/dkv/main.go

index 0273978..ac3b1f9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,39 @@
 Distributed Key Value Store using Consul to store application configuration data.
 
-# TODO
-# Add documentation on how to run.
+# TODO (Add documentation on how to run)
+
+# Sample Curl examples:
+
+## Load default configuration
+`curl -X GET localhost:8080/v1/config/load-default`
+
+## Register new domain
+`curl -X POST -d '{"domain":"<project>"}' localhost:8080/v1/register`
+`export TOKEN=`
+## Register new sub domain 
+`curl -X POST -d '{"subdomain":"<sub-project>"}' localhost:8080/v1/register/$TOKEN/subdomain`
+
+## Check if a domain is already registered.
+`curl -X GET localhost:8080/v1/register/$TOKEN`
+
+## List all sub domains in a domain.
+`TODO`
+
+## Upload properties file to domain or subdomain.
+`curl -X POST -F 'token=$TOKEN' -F 'configFile=@./example.properties' localhost:8080/v1/config`
+`curl -X POST -F 'token=$TOKEN' -F 'subdomain=<sub-domain>' -F 'configFile=@./example.properties' localhost:8080/v1/config`
+
+## Load properties file into Consul
+`curl -X POST -d '{"token":"$TOKEN", "filename": "example.properties"}' localhost:8080/v1/config/load`
+
+## Fetch properties file
+`curl -X GET localhost:8080/v1/config/$TOKEN/example.properties`
+`curl -X GET localhost:8080/v1/config/$TOKEN/<sub-domain>/example.properties`
+
+## Delete properties file
+`curl -X DELETE localhost:8080/v1/config/$TOKEN/example.properties`
+`curl -X DELETE localhost:8080/v1/config/$TOKEN/<sub-domain>/example.properties`
+
+## Delete project/sub project
+`curl -X DELETE localhost:8080/v1/register/$TOKEN/subdomain/<sub-domain>`
+`curl -X DELETE localhost:8080/v1/register/$TOKEN`
index 535922e..687a4d3 100644 (file)
   packages = ["."]
   revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00"
 
+[[projects]]
+  branch = "master"
+  name = "github.com/hashicorp/go-uuid"
+  packages = ["."]
+  revision = "27454136f0364f2d44b1276c552d69105cf8c498"
+
 [[projects]]
   name = "github.com/hashicorp/serf"
   packages = ["coordinate"]
@@ -76,6 +82,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "000a855a3e5fca6c1f7b134951013777f7decbbde7be20af1e262da6005e874a"
+  inputs-digest = "ec895bb8f93ec798d68a3d83b9004eff00dc4078f5a2bccf75390bcb8fccbd72"
   solver-name = "gps-cdcl"
   solver-version = 1
similarity index 96%
rename from src/dkv/api/consulConnection.go
rename to src/dkv/api/backendConsulConnection.go
index 5ea79fd..9c2f8d6 100644 (file)
@@ -49,6 +49,9 @@ the initialise.go file where we are creating a ConsulStruct and assigning it to
 above.
 */
 func (c *ConsulStruct) InitializeConsulClient() error {
+       if os.Getenv("CONSUL_IP") == "" {
+               return errors.New("CONSUL_IP environment variable not set.")
+       }
        config := consulapi.DefaultConfig()
        config.Address = os.Getenv("CONSUL_IP") + ":8500"
 
diff --git a/src/dkv/api/backendFilesystemConnection.go b/src/dkv/api/backendFilesystemConnection.go
new file mode 100644 (file)
index 0000000..f09e74f
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "errors"
+       uuid "github.com/hashicorp/go-uuid"
+       "net/http"
+       "os"
+)
+
+type DirectoryOperationer interface {
+       // Service Operations.
+       CreateService(CreateRegisterServiceBody) (string, error)
+       RemoveService(string) error
+       CreateServiceSubdomain(string, string) error
+       RemoveServiceSubdomain(string, string) error
+       // Directory Operations.
+       CreateDirectory(string) error
+       RemoveDirectory(string) error
+       RemoveSubDirectory(string, string) error
+       RemoveFile(string, string, string) error
+       FindService(string) (string, bool, error)
+       FetchFile(http.ResponseWriter, *http.Request, string, string, string)
+       CreateFile(string) (*os.File, error)
+}
+
+type DirectoryStruct struct {
+       directory string
+}
+
+const (
+       MOUNTPATH = "../../mountpath/"
+       JSONPATH  = "api/token_service_map.json"
+)
+
+var Directory DirectoryOperationer
+
+func (d *DirectoryStruct) CreateService(body CreateRegisterServiceBody) (string, error) {
+
+       // Having same name is prohibited?
+       found, err := FindServiceInJSON(JSONPATH, body.Domain)
+       if err != nil {
+               return "", err
+       }
+       if found {
+               return "", errors.New("Service already found. Check name.")
+       }
+
+       token, err := uuid.GenerateUUID()
+       if err != nil {
+               return "", err
+       }
+
+       err = d.CreateDirectory(token)
+       if err != nil {
+               return "", err
+       }
+
+       err = WriteJSON(JSONPATH, token, body.Domain)
+
+       if err != nil {
+               return "", err
+       }
+       return token, nil
+}
+
+func (d *DirectoryStruct) CreateServiceSubdomain(token string, subdomain string) error {
+       foundToken, err := FindTokenInJSON(JSONPATH, token)
+       if err != nil {
+               return err
+       }
+       if foundToken == false {
+               return errors.New("Token not found. Please check token or if service is created.")
+       }
+       err = d.CreateSubDirectory(token, subdomain)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) RemoveService(token string) error {
+       err := DeleteInJSON(JSONPATH, token)
+       if err != nil {
+               return err
+       }
+       err = d.RemoveDirectory(token)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) FindService(token string) (string, bool, error) {
+       service, found, err := GetServicebyToken(JSONPATH, token)
+       if err != nil {
+               return "", false, err
+       }
+       return service, found, nil
+}
+
+func (d *DirectoryStruct) RemoveServiceSubdomain(token string, subdomain string) error {
+       foundToken, err := FindTokenInJSON(JSONPATH, token)
+       if err != nil {
+               return err
+       }
+       if foundToken == false {
+               return errors.New("Token not found. Please check token or if service is created.")
+       }
+       err = d.RemoveSubDirectory(token, subdomain)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) CreateDirectory(token string) error {
+       // Permissions inside mount point?
+       err := os.Mkdir(MOUNTPATH+token, os.FileMode(0770))
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) CreateSubDirectory(token string, subdomain string) error {
+       err := os.Mkdir(MOUNTPATH+token+"/"+subdomain, os.FileMode(0770))
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) RemoveDirectory(token string) error {
+       err := os.RemoveAll(MOUNTPATH + token)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) RemoveSubDirectory(token string, subdomain string) error {
+       err := os.RemoveAll(MOUNTPATH + token + "/" + subdomain)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) RemoveFile(token string, subdomain string, filename string) error {
+       var filepath = ""
+       if subdomain != "" {
+               filepath += MOUNTPATH + token + "/" + subdomain + "/" + filename
+       } else {
+               filepath += MOUNTPATH + token + "/" + filename
+       }
+       // If error, it seems to show the mounthpath back to the client. This is not good
+       // error return practise. It shoudn't return the exact file path on the system.
+       err := os.Remove(filepath)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (d *DirectoryStruct) FetchFile(
+       w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+
+       var filepath = ""
+       if subdomain != "" {
+               filepath += MOUNTPATH + token + "/" + subdomain + "/" + filename
+       } else {
+               filepath += MOUNTPATH + token + "/" + filename
+       }
+
+       http.ServeFile(w, r, filepath)
+}
+
+func (d *DirectoryStruct) CreateFile(filepath string) (*os.File, error) {
+       f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0770)
+       if err != nil {
+               return nil, err
+       }
+       return f, nil
+}
diff --git a/src/dkv/api/backendPropertiesConnection.go b/src/dkv/api/backendPropertiesConnection.go
new file mode 100644 (file)
index 0000000..24140cd
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "errors"
+       "github.com/magiconair/properties"
+       "io/ioutil"
+       "log"
+       "os"
+       "sync"
+)
+
+type KeyValuesInterface interface {
+       WriteKVsToConsul(string, string) error
+       ConfigReader(string, string, string) error
+       ReadMultiplePropertiesRecursive(string) error
+       ReadMultipleProperties(string) error
+       ReadProperty(string) error
+}
+
+type KeyValuesStruct struct {
+       sync.RWMutex
+       kvs map[string]string
+}
+
+var KeyValues KeyValuesInterface
+
+func (kvStruct *KeyValuesStruct) WriteKVsToConsul(token string, subdomain string) error {
+       var prefix = ""
+       if subdomain != "" {
+               prefix += token + "/" + subdomain
+       } else {
+               prefix += token + "/"
+       }
+       for key, value := range kvStruct.kvs {
+               key = prefix + key
+               err := Consul.RequestPUT(key, value)
+               if err != nil {
+                       return err
+               }
+               log.Println("[INFO] Key: ", key, "| Value: ", value)
+       }
+       log.Println("[INFO] Wrote KVs to Consul.")
+       return nil
+}
+
+func (kvStruct *KeyValuesStruct) ConfigReader(token string, subdomain string, filename string) error {
+       defer kvStruct.Unlock()
+
+       kvStruct.Lock()
+       var filepath = MOUNTPATH
+
+       if filename != "" && subdomain != "" {
+               // Specific file in specific domain.
+               filepath += token + "/" + subdomain + "/" + filename
+               err := kvStruct.ReadProperty(filepath)
+               if err != nil {
+                       return err
+               }
+               return nil
+       }
+
+       if filename != "" && subdomain == "" {
+               // Specific file in Token
+               filepath += token + "/" + filename
+               err := kvStruct.ReadProperty(filepath)
+               if err != nil {
+                       return err
+               }
+               return nil
+       }
+
+       if filename == "" && subdomain != "" {
+               // All files in specific domain
+               filepath += token + "/" + subdomain
+               err := kvStruct.ReadMultipleProperties(filepath)
+               if err != nil {
+                       return err
+               }
+       }
+
+       filepath += token
+       err := kvStruct.ReadMultiplePropertiesRecursive(filepath)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadMultiplePropertiesRecursive(path string) error {
+       // Go inside each sub directory and run ReadMultipleProperties inside.
+       files, err := ioutil.ReadDir(path)
+       if err != nil {
+               return err
+       }
+
+       for _, f := range files {
+               fi, _ := os.Stat(path + "/" + f.Name())
+               if fi.Mode().IsDir() {
+                       kvStruct.ReadMultipleProperties(path + "/" + f.Name())
+               } else {
+                       kvStruct.ReadProperty(path + "/" + f.Name())
+               }
+       }
+       return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadMultipleProperties(path string) error {
+       files, err := ioutil.ReadDir(path)
+       if err != nil {
+               return err
+       }
+
+       for _, f := range files {
+               kvStruct.ReadProperty(path + f.Name())
+       }
+
+       return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadProperty(path string) error {
+       _, err := os.Stat(path)
+       if err != nil {
+               return errors.New("File does not exists.")
+       }
+       p := properties.MustLoadFile(path, properties.UTF8)
+       for _, key := range p.Keys() {
+               kvStruct.kvs[key] = p.MustGet(key)
+       }
+       return nil
+}
diff --git a/src/dkv/api/backendfakes.go b/src/dkv/api/backendfakes.go
new file mode 100644 (file)
index 0000000..5415608
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "errors"
+       "net/http"
+)
+
+/*
+A ConsulStruct is added inside this so that FakeConsul becomes an implementation of the Consul interface.
+If we don't add ConsulStruct inside this, it complains that the FakeConsul Struct doesn't implement all the methods
+defined in Consul interface.
+*/
+// Correct
+type FakeConsul struct {
+       ConsulStruct
+}
+
+func (f *FakeConsul) RequestGETS() ([]string, error) {
+       return []string{"key1", "key2"}, nil
+}
+
+func (f *FakeConsul) RequestGET(key string) (string, error) {
+       return key, nil
+}
+
+func (f *FakeConsul) RequestPUT(key string, value string) error {
+       return nil
+}
+
+func (f *FakeConsul) RequestDELETE(key string) error {
+       return nil
+}
+
+// Error
+type FakeConsulErr struct {
+       ConsulStruct
+}
+
+func (f *FakeConsulErr) RequestGETS() ([]string, error) {
+       return []string{"", ""}, errors.New("Internal Server Error")
+}
+
+func (f *FakeConsulErr) RequestGET(key string) (string, error) {
+       return "", errors.New("Internal Server Error")
+}
+
+func (f *FakeConsulErr) RequestDELETE(key string) error {
+       return errors.New("Internal Server Error")
+}
+
+/*
+This is done similar to the fake Consul above to pass FakeKeyValues to the interface and control method's outputs
+as required.
+*/
+//Correct
+type FakeKeyValues struct {
+       KeyValuesStruct
+}
+
+func (f *FakeKeyValues) ConfigReader(token string, subdomain string, filename string) error {
+       return nil
+}
+
+func (f *FakeKeyValues) WriteKVsToConsul(token string, subdomain string) error {
+       return nil
+}
+
+// Error
+type FakeKeyValuesErr struct {
+       KeyValuesStruct
+}
+
+func (f *FakeKeyValuesErr) ConfigReader(token string, subdomain string, filename string) error {
+       return errors.New("Internal Server Error")
+}
+
+func (f *FakeKeyValuesErr) WriteKVsToConsul(token string, subdomain string) error {
+       return errors.New("Internal Server Error")
+}
+
+// Correct
+type FakeDirectory struct {
+       DirectoryStruct
+}
+
+func (f *FakeDirectory) CreateService(CreateRegisterServiceBody) (string, error) {
+       return "", nil
+}
+
+func (f *FakeDirectory) RemoveService(token string) error {
+       return nil
+}
+
+func (f *FakeDirectory) CreateServiceSubdomain(token string, subdomain string) error {
+       return nil
+}
+
+func (f *FakeDirectory) RemoveServiceSubdomain(token string, subdomain string) error {
+       return nil
+}
+
+func (f *FakeDirectory) FindService(token string) (string, bool, error) {
+       return "service1", true, nil
+}
+
+func (f *FakeDirectory) FetchFile(
+       w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+}
+
+func (f *FakeDirectory) RemoveFile(token string, subdomain string, filename string) error {
+       return nil
+}
+
+// Error
+type FakeDirectoryErr struct {
+       DirectoryStruct
+}
+
+func (f *FakeDirectoryErr) CreateService(CreateRegisterServiceBody) (string, error) {
+       return "", errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) RemoveService(token string) error {
+       return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) CreateServiceSubdomain(token string, subdomain string) error {
+       return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) RemoveServiceSubdomain(token string, subdomain string) error {
+       return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) FindService(token string) (string, bool, error) {
+       return "", false, errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) FetchFile(
+       w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+
+}
+
+func (f *FakeDirectoryErr) RemoveFile(token string, subdomain string, filename string) error {
+       return errors.New("Internal Server Error.")
+}
diff --git a/src/dkv/api/configHandlers.go b/src/dkv/api/configHandlers.go
new file mode 100644 (file)
index 0000000..f5bac34
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "encoding/json"
+       "errors"
+       "github.com/gorilla/mux"
+       "io"
+       "mime/multipart"
+       "net/http"
+)
+
+type UploadConfigBody struct {
+       Token     string
+       File      multipart.File
+       Subdomain string
+}
+
+type LoadConfigBody struct {
+       Token     string `json:"token"`
+       Filename  string `json:"filename"`
+       Subdomain string `json:"subdomain"`
+}
+
+func ValidateLoadConfigBody(body LoadConfigBody) error {
+       if body.Token == "" {
+               return errors.New("Token not set. Please set Token in POST.")
+       }
+       return nil
+}
+
+func HandleConfigUpload(w http.ResponseWriter, r *http.Request) {
+       r.ParseMultipartForm(100000) // 2k bytes?
+       file, handler, err := r.FormFile("configFile")
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, "Error in uploaded file.")
+               return
+       }
+       defer file.Close()
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+               return
+       }
+
+       token := r.Form.Get("token")
+       subdomain := r.Form.Get("subdomain")
+
+       if token == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Token not present in Form data.")
+               return
+       }
+
+       var filename = ""
+       if subdomain != "" {
+               filename += token + "/" + subdomain + "/" + handler.Filename
+       } else {
+               filename += token + "/" + handler.Filename
+       }
+
+       f, err := Directory.CreateFile(MOUNTPATH + filename)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+               return
+       }
+       defer f.Close()
+       io.Copy(f, file)
+}
+
+func HandleConfigLoad(w http.ResponseWriter, r *http.Request) {
+
+       var body LoadConfigBody
+
+       decoder := json.NewDecoder(r.Body)
+       err := decoder.Decode(&body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+               return
+       }
+
+       err = ValidateLoadConfigBody(body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusBadRequest, string(err.Error()))
+               return
+       }
+
+       err = KeyValues.ConfigReader(body.Token, body.Subdomain, body.Filename)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+               return
+       }
+
+       err = KeyValues.WriteKVsToConsul(body.Token, body.Subdomain)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Configuration read and Key Values loaded to Consul.")
+       }
+}
+
+func HandleDefaultConfigLoad(w http.ResponseWriter, r *http.Request) {
+       err := KeyValues.ConfigReader("default", "", "")
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+               return
+       }
+       err = KeyValues.WriteKVsToConsul("default", "")
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Default Configuration read and default Key Values loaded to Consul.")
+       }
+}
+
+func HandleConfigGet(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+       filename := vars["filename"]
+       subdomain := vars["subdomain"]
+
+       if token == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+               return
+       }
+
+       if filename == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "filename not passed.")
+               return
+       }
+
+       Directory.FetchFile(w, r, token, subdomain, filename)
+}
+
+func HandleConfigDelete(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+       filename := vars["filename"]
+       subdomain := vars["subdomain"]
+
+       if token == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+               return
+       }
+
+       if filename == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "filename not passed.")
+               return
+       }
+
+       err := Directory.RemoveFile(token, subdomain, filename)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Deletion of config is successful.")
+       }
+}
diff --git a/src/dkv/api/configHandlers_test.go b/src/dkv/api/configHandlers_test.go
new file mode 100644 (file)
index 0000000..e2f796a
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "bytes"
+       "encoding/json"
+       "github.com/gorilla/mux"
+       "github.com/stretchr/testify/assert"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+)
+
+func RouterConfig() *mux.Router {
+       router := mux.NewRouter()
+       router.HandleFunc("/v1/config", HandleConfigUpload).Methods("POST")
+       router.HandleFunc("/v1/config/{token}/{filename}", HandleConfigGet).Methods("GET")
+       router.HandleFunc("/v1/config/{token}/{filename}", HandleConfigDelete).Methods("DELETE")
+       router.HandleFunc("/v1/config/load", HandleConfigLoad).Methods("POST")
+       router.HandleFunc("/v1/config/load-default", HandleDefaultConfigLoad).Methods("GET")
+       return router
+}
+
+func TestHandleConfigGet(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("GET", "/v1/config/token1/filename1", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+func TestHandleConfigDelete(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/config/token1/filename1", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigDelete_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectoryErr{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/config/token1/filename1", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleConfigPOST(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsul{}
+       KeyValues = &FakeKeyValues{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       body := &LoadConfigBody{
+               Token:     "test",
+               Filename:  "test",
+               Subdomain: "test",
+       }
+
+       b, _ := json.Marshal(body)
+
+       // json Marshal converts struct to json in Bytes. But bytes doesn't have
+       // io reader needed. So the byte is passed to NewBuffer.
+       request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigPOST_only_token(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsul{}
+       KeyValues = &FakeKeyValues{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       body := &LoadConfigBody{
+               Token:     "test",
+               Filename:  "",
+               Subdomain: "",
+       }
+
+       b, _ := json.Marshal(body)
+
+       // json Marshal converts struct to json in Bytes. But bytes doesn't have
+       // io reader needed. So the byte is passed to NewBuffer.
+       request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigPOST_no_body(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsul{}
+       KeyValues = &FakeKeyValues{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       body := &LoadConfigBody{}
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleConfigPOST_ConsulError(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsulErr{}
+       KeyValues = &FakeKeyValuesErr{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       body := &LoadConfigBody{
+               Token:     "test",
+               Filename:  "test",
+               Subdomain: "test",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleConfigUpload_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("POST", "/v1/config", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleDefaultConfigLoad(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsul{}
+       KeyValues = &FakeKeyValues{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       request, _ := http.NewRequest("GET", "/v1/config/load-default", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleDefaultConfigLoad_err(t *testing.T) {
+       oldConsul := Consul
+       oldKeyValues := KeyValues
+
+       Consul = &FakeConsul{}
+       KeyValues = &FakeKeyValuesErr{}
+
+       defer func() {
+               Consul = oldConsul
+               KeyValues = oldKeyValues
+       }()
+
+       request, _ := http.NewRequest("GET", "/v1/config/load-default", nil)
+       response := httptest.NewRecorder()
+       RouterConfig().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
diff --git a/src/dkv/api/consulConnection_test.go b/src/dkv/api/consulConnection_test.go
deleted file mode 100644 (file)
index cc973ce..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2018 Intel Corporation, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package api
diff --git a/src/dkv/api/endpointViews_fake.go b/src/dkv/api/endpointViews_fake.go
deleted file mode 100644 (file)
index ecc6466..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2018 Intel Corporation, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package api
-
-import "errors"
-
-/*
-A ConsulStruct is added inside this so that FakeConsul becomes an implementation of the Consul interface.
-If we don't add ConsulStruct inside this, it complains that the FakeConsul Struct doesn't implement all the methods
-defined in Consul interface.
-*/
-// Correct
-type FakeConsul struct {
-       ConsulStruct
-}
-
-func (f *FakeConsul) RequestGETS() ([]string, error) {
-       return []string{"key1", "key2"}, nil
-}
-
-func (f *FakeConsul) RequestGET(key string) (string, error) {
-       return key, nil
-}
-
-func (f *FakeConsul) RequestPUT(key string, value string) error {
-       return nil
-}
-
-func (f *FakeConsul) RequestDELETE(key string) error {
-       return nil
-}
-
-// Error
-type FakeConsulErr struct {
-       ConsulStruct
-}
-
-func (f *FakeConsulErr) RequestGETS() ([]string, error) {
-       return []string{"", ""}, errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestGET(key string) (string, error) {
-       return "", errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestPUT(key string, value string) error {
-       return errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestDELETE(key string) error {
-       return errors.New("Internal Server Error")
-}
-
-/*
-This is done similar to the fake Consul above to pass FakeKeyValues to the interface and control method's outputs
-as required.
-*/
-//Correct
-type FakeKeyValues struct {
-       KeyValuesStruct
-}
-
-func (f *FakeKeyValues) ReadConfigs(body POSTBodyStruct) error {
-       return nil
-}
-
-func (f *FakeKeyValues) WriteKVsToConsul(prefix string) error {
-       return nil
-}
-
-// Error
-type FakeKeyValuesErr struct {
-       KeyValuesStruct
-}
-
-func (f *FakeKeyValuesErr) ReadConfigs(body POSTBodyStruct) error {
-       return errors.New("Internal Server Error")
-}
-
-func (f *FakeKeyValuesErr) WriteKVsToConsul(prefix string) error {
-       return errors.New("Internal Server Error")
-}
diff --git a/src/dkv/api/endpointViews_test.go b/src/dkv/api/endpointViews_test.go
deleted file mode 100644 (file)
index 8e2799e..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2018 Intel Corporation, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package api
-
-import (
-       "bytes"
-       "encoding/json"
-       "github.com/gorilla/mux"
-       "github.com/stretchr/testify/assert"
-       "net/http"
-       "net/http/httptest"
-       "testing"
-)
-
-func Router() *mux.Router {
-       router := mux.NewRouter()
-       router.HandleFunc("/loadconfigs", HandlePOST).Methods("POST")
-       router.HandleFunc("/getconfig/{key}", HandleGET).Methods("GET")
-       router.HandleFunc("/deleteconfig/{key}", HandleDELETE).Methods("DELETE")
-       router.HandleFunc("/getconfigs", HandleGETS).Methods("GET")
-       return router
-}
-
-func TestHandleGETS(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsul{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("GET", "/getconfigs", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleGETS_err(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsulErr{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("GET", "/getconfigs", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandleGET(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsul{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("GET", "/getconfig/key1", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleGET_err(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsulErr{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("GET", "/getconfig/key1", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST(t *testing.T) {
-       oldConsul := Consul
-       oldKeyValues := KeyValues
-
-       Consul = &FakeConsul{}
-       KeyValues = &FakeKeyValues{}
-
-       defer func() {
-               Consul = oldConsul
-               KeyValues = oldKeyValues
-       }()
-
-       body := &POSTBodyStruct{
-               Domain: "test",
-               Type: &TypeStruct{
-                       FilePath: "default",
-               },
-       }
-
-       b, _ := json.Marshal(body)
-
-       // json Marshal converts struct to json in Bytes. But bytes doesn't have
-       // io reader needed. So the byte is passed to NewBuffer.
-       request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandlePOST_no_body(t *testing.T) {
-       oldConsul := Consul
-       oldKeyValues := KeyValues
-
-       Consul = &FakeConsul{}
-       KeyValues = &FakeKeyValues{}
-
-       defer func() {
-               Consul = oldConsul
-               KeyValues = oldKeyValues
-       }()
-
-       body := &POSTBodyStruct{}
-
-       b, _ := json.Marshal(body)
-
-       request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST_no_filepath(t *testing.T) {
-       oldConsul := Consul
-       oldKeyValues := KeyValues
-
-       Consul = &FakeConsul{}
-       KeyValues = &FakeKeyValues{}
-
-       defer func() {
-               Consul = oldConsul
-               KeyValues = oldKeyValues
-       }()
-
-       body := &POSTBodyStruct{
-               Type: &TypeStruct{},
-       }
-
-       b, _ := json.Marshal(body)
-
-       request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST_ConsulError(t *testing.T) {
-       oldConsul := Consul
-       oldKeyValues := KeyValues
-
-       Consul = &FakeConsulErr{}
-       KeyValues = &FakeKeyValuesErr{}
-
-       defer func() {
-               Consul = oldConsul
-               KeyValues = oldKeyValues
-       }()
-
-       body := &POSTBodyStruct{
-               Domain: "test",
-               Type: &TypeStruct{
-                       FilePath: "default",
-               },
-       }
-
-       b, _ := json.Marshal(body)
-
-       request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 500, response.Code, "500 response is expected")
-}
-
-func TestHandleDELETE(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsul{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("DELETE", "/deleteconfig/key1", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleDELETE_err(t *testing.T) {
-       oldConsul := Consul
-       Consul = &FakeConsulErr{}
-       defer func() { Consul = oldConsul }()
-
-       request, _ := http.NewRequest("DELETE", "/deleteconfig/key1", nil)
-       response := httptest.NewRecorder()
-       Router().ServeHTTP(response, request)
-
-       assert.Equal(t, 400, response.Code, "400 response is expected")
-}
index 331c981..824ca81 100644 (file)
 
 package api
 
-import (
-       "errors"
-       "os"
-)
-
 func Initialise() error {
-       if os.Getenv("CONSUL_IP") == "" {
-               return errors.New("CONSUL_IP environment variable not set.")
-       }
-
        Consul = &ConsulStruct{}
        KeyValues = &KeyValuesStruct{kvs: make(map[string]string)}
+       Directory = &DirectoryStruct{directory: ""}
 
        err := Consul.InitializeConsulClient()
        if err != nil {
diff --git a/src/dkv/api/propertiesReader.go b/src/dkv/api/propertiesReader.go
deleted file mode 100644 (file)
index 559c0fc..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2018 Intel Corporation, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package api
-
-import (
-       "errors"
-       "github.com/magiconair/properties"
-       "io/ioutil"
-       "log"
-       "path"
-       "runtime"
-       "sync"
-)
-
-type KeyValuesInterface interface {
-       WriteKVsToConsul(string) error
-       ReadConfigs(POSTBodyStruct) error
-       PropertiesFilesToKV(string) error
-       ReadMultipleProperties(string) error
-       ReadProperty(string)
-}
-
-type KeyValuesStruct struct {
-       sync.RWMutex
-       kvs map[string]string
-}
-
-var KeyValues KeyValuesInterface
-
-func (kvStruct *KeyValuesStruct) WriteKVsToConsul(prefix string) error {
-       for key, value := range kvStruct.kvs {
-               key = prefix + "." + key
-               err := Consul.RequestPUT(key, value)
-               if err != nil {
-                       return err
-               }
-               log.Println("[INFO] Key: ", key, "| Value: ", value)
-       }
-       log.Println("[INFO] Wrote KVs to Consul.")
-       return nil
-}
-
-func (kvStruct *KeyValuesStruct) ReadConfigs(body POSTBodyStruct) error {
-       defer kvStruct.Unlock()
-
-       kvStruct.Lock()
-
-       err := kvStruct.PropertiesFilesToKV(body.Type.FilePath)
-       if err != nil {
-               return err
-       }
-       return nil
-}
-
-func (kvStruct *KeyValuesStruct) PropertiesFilesToKV(directory string) error {
-
-       if directory == "default" {
-               _, filename, _, ok := runtime.Caller(0)
-               if !ok {
-                       return errors.New("No caller")
-               }
-
-               defaultDir := path.Dir(filename) + "/../configurations/"
-               err := kvStruct.ReadMultipleProperties(defaultDir)
-               if err != nil {
-                       return err
-               }
-
-               return nil
-
-       } else {
-               directory += "/"
-               err := kvStruct.ReadMultipleProperties(directory)
-               if err != nil {
-                       return err
-               }
-
-               return nil
-       }
-}
-
-func (kvStruct *KeyValuesStruct) ReadMultipleProperties(path string) error {
-       files, err := ioutil.ReadDir(path)
-       if err != nil {
-               return err
-       }
-
-       for _, f := range files {
-               kvStruct.ReadProperty(path + f.Name())
-       }
-
-       return nil
-}
-
-func (kvStruct *KeyValuesStruct) ReadProperty(path string) {
-       p := properties.MustLoadFile(path, properties.UTF8)
-       for _, key := range p.Keys() {
-               kvStruct.kvs[key] = p.MustGet(key)
-       }
-}
diff --git a/src/dkv/api/propertiesReader_test.go b/src/dkv/api/propertiesReader_test.go
deleted file mode 100644 (file)
index 342542a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2018 Intel Corporation, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package api
-
-// TODO(sshank)
similarity index 56%
rename from src/dkv/api/endpointViews.go
rename to src/dkv/api/queryConsulHandlers.go
index 8f77061..fb63b6d 100644 (file)
@@ -18,7 +18,6 @@ package api
 
 import (
        "encoding/json"
-       "errors"
        "github.com/gorilla/mux"
        "net/http"
 )
@@ -35,80 +34,9 @@ type ResponseGETSStruct struct {
        Response []string `json:"response"`
 }
 
-type POSTBodyStruct struct {
-       Domain string      `json:"domain"`
-       Type   *TypeStruct `json:"type"`
-}
-
-type TypeStruct struct {
-       FilePath string `json:"file_path"`
-}
-
-func ValidateBody(body POSTBodyStruct) error {
-       if body.Domain == "" {
-               return errors.New("Domain not set. Please set domain in POST.")
-       }
-       if body.Type == nil {
-               return errors.New("Type not set. Recheck POST data.")
-       } else if body.Type.FilePath == "" {
-               return errors.New("file_path not set")
-       } else {
-               return nil
-       }
-}
-
-func HandlePOST(w http.ResponseWriter, r *http.Request) {
-
-       var body POSTBodyStruct
-
-       decoder := json.NewDecoder(r.Body)
-       err := decoder.Decode(&body)
-
-       if err != nil {
-               req := ResponseStringStruct{Response: "Empty body."}
-               w.Header().Set("Content-Type", "application/json")
-               w.WriteHeader(http.StatusBadRequest)
-               json.NewEncoder(w).Encode(&req)
-               return
-       }
-
-       err = ValidateBody(body)
-
-       if err != nil {
-               req := ResponseStringStruct{Response: string(err.Error())}
-               w.Header().Set("Content-Type", "application/json")
-               w.WriteHeader(http.StatusBadRequest)
-               json.NewEncoder(w).Encode(req)
-               return
-       }
-
-       err = KeyValues.ReadConfigs(body)
-
-       if err != nil {
-               req := ResponseStringStruct{Response: string(err.Error())}
-               w.Header().Set("Content-Type", "application/json")
-               w.WriteHeader(http.StatusInternalServerError)
-               json.NewEncoder(w).Encode(req)
-               return
-       }
-
-       err = KeyValues.WriteKVsToConsul(body.Domain)
-
-       if err != nil {
-               req := ResponseStringStruct{Response: string(err.Error())}
-               w.Header().Set("Content-Type", "application/json")
-               w.WriteHeader(http.StatusInternalServerError)
-               json.NewEncoder(w).Encode(req)
-       } else {
-               req := ResponseStringStruct{Response: "Configuration read and default Key Values loaded to Consul"}
-               w.Header().Set("Content-Type", "application/json")
-               json.NewEncoder(w).Encode(&req)
-       }
-}
-
 func HandleGET(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
-       key := vars["key"]
+       key := vars["token"] + "/" + vars["key"]
 
        value, err := Consul.RequestGET(key)
 
diff --git a/src/dkv/api/queryConsulHandlers_test.go b/src/dkv/api/queryConsulHandlers_test.go
new file mode 100644 (file)
index 0000000..38f3acd
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "github.com/gorilla/mux"
+       "github.com/stretchr/testify/assert"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+)
+
+func RouterConsul() *mux.Router {
+       router := mux.NewRouter()
+       router.HandleFunc("/v1/getconfig/{key}", HandleGET).Methods("GET")
+       router.HandleFunc("/v1/deleteconfig/{key}", HandleDELETE).Methods("DELETE")
+       router.HandleFunc("/v1/getconfigs", HandleGETS).Methods("GET")
+       return router
+}
+
+func TestHandleGETS(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsul{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("GET", "/v1/getconfigs", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleGETS_err(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsulErr{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("GET", "/v1/getconfigs", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleGET(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsul{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("GET", "/v1/getconfig/key1", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleGET_err(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsulErr{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("GET", "/v1/getconfig/key1", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleDELETE(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsul{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/deleteconfig/key1", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleDELETE_err(t *testing.T) {
+       oldConsul := Consul
+       Consul = &FakeConsulErr{}
+       defer func() { Consul = oldConsul }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/deleteconfig/key1", nil)
+       response := httptest.NewRecorder()
+       RouterConsul().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
diff --git a/src/dkv/api/registrationHandlers.go b/src/dkv/api/registrationHandlers.go
new file mode 100644 (file)
index 0000000..1c50b33
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "encoding/json"
+       "errors"
+       "github.com/gorilla/mux"
+       "net/http"
+)
+
+type CreateRegisterServiceBody struct {
+       Domain string `json:"domain"`
+}
+
+type CreateServiceSubdomainBody struct {
+       Subdomain string `json:"subdomain"`
+}
+
+func ValidateCreateRegisterServiceBody(body CreateRegisterServiceBody) error {
+       if body.Domain == "" {
+               return errors.New("Domain not set. Please set domain in POST.")
+       }
+       if body.Domain == "default" {
+               return errors.New("Domain not allowed. Please set another domain in POST.")
+       }
+       return nil
+}
+
+/*
+       TODO(sshank): Add validations to check if tokens/sub-domains/files indeed
+       exist in the token_service JSON or in the directory. This is to avoid the service
+       returning the file system errors to the user.
+*/
+
+func HandleServiceCreate(w http.ResponseWriter, r *http.Request) {
+       var body CreateRegisterServiceBody
+
+       decoder := json.NewDecoder(r.Body)
+       err := decoder.Decode(&body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+               return
+       }
+
+       err = ValidateCreateRegisterServiceBody(body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusBadRequest, string(err.Error()))
+               return
+       }
+
+       token, err := Directory.CreateService(body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Registration Successful. Token: "+token)
+       }
+}
+
+func HandleServiceGet(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+
+       if token == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Token not present in path.")
+               return
+       }
+
+       service, found, err := Directory.FindService(token)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               if found == true {
+                       GenerateResponse(w, r, http.StatusOK, service)
+               } else {
+                       GenerateResponse(w, r, http.StatusNotFound, "Service for Token:"+token+"not found.")
+               }
+
+       }
+}
+
+func HandleServiceDelete(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+
+       if token == "default" {
+               GenerateResponse(w, r, http.StatusNotAcceptable, "Default delete not allowed.")
+               return
+       }
+
+       err := Directory.RemoveService(token)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Deletion of service is successful.")
+       }
+}
+
+func HandleServiceSubdomainCreate(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+
+       var body CreateServiceSubdomainBody
+
+       decoder := json.NewDecoder(r.Body)
+       err := decoder.Decode(&body)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+               return
+       }
+
+       if body.Subdomain == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Subdomain not found in POST.")
+               return
+       }
+
+       err = Directory.CreateServiceSubdomain(token, body.Subdomain)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Subdomain creation success with token: "+token)
+       }
+
+}
+
+func HandleServiceSubdomainGet(w http.ResponseWriter, r *http.Request) {
+       // TODO(sshank): This will list all subdomain in a service.
+}
+
+func HandleServiceSubdomainDelete(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       token := vars["token"]
+       subdomain := vars["subdomain"]
+
+       if token == "" {
+               GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+               return
+       }
+
+       if token == "default" && subdomain == "" {
+               GenerateResponse(w, r, http.StatusNotAcceptable, "Not allowerd.")
+               return
+       }
+
+       err := Directory.RemoveServiceSubdomain(token, subdomain)
+
+       if err != nil {
+               GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+       } else {
+               GenerateResponse(w, r, http.StatusOK, "Deletion of service is successful.")
+       }
+}
diff --git a/src/dkv/api/registrationHandlers_test.go b/src/dkv/api/registrationHandlers_test.go
new file mode 100644 (file)
index 0000000..3482e8d
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package api
+
+import (
+       "bytes"
+       "encoding/json"
+       "github.com/gorilla/mux"
+       "github.com/stretchr/testify/assert"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+)
+
+func RouterRegister() *mux.Router {
+       router := mux.NewRouter()
+       router.HandleFunc("/v1/register", HandleServiceCreate).Methods("POST")
+       router.HandleFunc("/v1/register/{token}", HandleServiceGet).Methods("GET")
+       router.HandleFunc("/v1/register/{token}", HandleServiceDelete).Methods("DELETE")
+       return router
+}
+
+func RouterRegisterSubdomain() *mux.Router {
+       router := mux.NewRouter()
+       router.HandleFunc("/v1/register/{token}/subdomain", HandleServiceSubdomainCreate).Methods("POST")
+       router.HandleFunc("/v1/register/{token}/subdomain/{subdomain}", HandleServiceSubdomainDelete).Methods("DELETE")
+       return router
+}
+
+func TestHandleServiceCreate(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateRegisterServiceBody{
+               Domain: "test",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceCreate_default(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateRegisterServiceBody{
+               Domain: "default",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+func TestHandleServiceCreate_no_body(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateRegisterServiceBody{}
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceCreate_no_domain(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateRegisterServiceBody{
+               Domain: "",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceGet(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("GET", "/v1/register/token1", nil)
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceGet_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectoryErr{}
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("GET", "/v1/register/token1", nil)
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+func TestHandleServiceDelete(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/register/token1", nil)
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceDelete_default(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/register/default", nil)
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 406, response.Code, "406 response is expected")
+}
+
+func TestHandleServiceDelete_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectoryErr{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/register/token1", nil)
+       response := httptest.NewRecorder()
+       RouterRegister().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateServiceSubdomainBody{
+               Subdomain: "test",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_no_body(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateServiceSubdomainBody{}
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_no_subdomain(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateServiceSubdomainBody{
+               Subdomain: "",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectoryErr{}
+
+       defer func() { Directory = oldDirectory }()
+
+       body := &CreateServiceSubdomainBody{
+               Subdomain: "test",
+       }
+
+       b, _ := json.Marshal(body)
+
+       request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleServiceSuddomainDelete(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectory{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/register/token1/subdomain/subdomain1", nil)
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceSuddomainDelete_err(t *testing.T) {
+       oldDirectory := Directory
+       Directory = &FakeDirectoryErr{}
+
+       defer func() { Directory = oldDirectory }()
+
+       request, _ := http.NewRequest("DELETE", "/v1/register/token1/subdomain/subdomain1", nil)
+       response := httptest.NewRecorder()
+       RouterRegisterSubdomain().ServeHTTP(response, request)
+
+       assert.Equal(t, 500, response.Code, "500 response is expected")
+}
diff --git a/src/dkv/api/token_service_map.json b/src/dkv/api/token_service_map.json
new file mode 100644 (file)
index 0000000..51d4dbb
--- /dev/null
@@ -0,0 +1 @@
+[{"token":"default","service":"default"}]
\ No newline at end of file
diff --git a/src/dkv/api/utils.go b/src/dkv/api/utils.go
new file mode 100644 (file)
index 0000000..c1094ab
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+       "encoding/json"
+       "errors"
+       "io/ioutil"
+       "net/http"
+)
+
+var (
+       IoutilRead  = ioutil.ReadFile
+       IoutilWrite = ioutil.WriteFile
+       JsonReader  = ReadJSON
+)
+
+type Token_service_map struct {
+       Token   string `json:"token"`
+       Service string `json:"service"`
+}
+
+func ReadJSON(path string) ([]Token_service_map, error) {
+       var tsm_list []Token_service_map
+       // raw, err := ioutil.ReadFile("./token_service_map.json")
+       raw, err := IoutilRead(path)
+       if err != nil {
+               return tsm_list, err
+       }
+       json.Unmarshal(raw, &tsm_list)
+       return tsm_list, nil
+}
+
+func WriteJSON(path string, token string, service string) error {
+       tsm_list, err := JsonReader(path)
+       if err != nil {
+               return err
+       }
+       var tsm Token_service_map
+       tsm.Token = token
+       tsm.Service = service
+       tsm_list = append(tsm_list, tsm)
+       raw, err := json.Marshal(tsm_list)
+       if err != nil {
+               return err
+       }
+       err = IoutilWrite(path, raw, 0644)
+       if err != nil {
+               return err
+       }
+       return nil
+}
+
+func DeleteInJSON(path string, token string) error {
+       serviceList, err := JsonReader(path)
+       if err != nil {
+               return err
+       }
+
+       var resultList []Token_service_map
+       var foundFlag = false
+
+       // Linear search for the token. If found set found flag. If not, keep
+       // copying the different values to resultList.
+       for _, service := range serviceList {
+               if service.Token == token {
+                       foundFlag = true
+               } else {
+                       resultList = append(resultList, service)
+               }
+       }
+
+       if foundFlag == false {
+               return errors.New("Service not found. Check if Token is correct or service is registered.")
+       } else {
+               // This is done to avoid writing 'null' in the json file.
+               if len(serviceList) == 1 {
+                       dummy := Token_service_map{Token: "", Service: ""}
+                       resultList = append(resultList, dummy)
+               }
+               raw, err := json.Marshal(resultList)
+               if err != nil {
+                       return err
+               }
+               err = IoutilWrite(path, raw, 0644)
+               if err != nil {
+                       return err
+               }
+               return nil
+       }
+}
+
+func FindTokenInJSON(path string, token string) (bool, error) {
+       serviceList, err := JsonReader(path)
+       if err != nil {
+               return false, err
+       }
+
+       for _, service := range serviceList {
+               if service.Token == token {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+func FindServiceInJSON(path string, serviceName string) (bool, error) {
+       serviceList, err := JsonReader(path)
+       if err != nil {
+               return false, err
+       }
+
+       for _, service := range serviceList {
+               if service.Service == serviceName {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+func GetServicebyToken(path string, token string) (string, bool, error) {
+       serviceList, err := JsonReader(path)
+       if err != nil {
+               return "", false, err
+       }
+       for _, service := range serviceList {
+               if service.Token == token {
+                       return service.Service, true, nil
+               }
+       }
+       return "", false, nil
+}
+
+func GenerateResponse(w http.ResponseWriter, r *http.Request, httpStatus int, msg string) {
+       req := ResponseStringStruct{Response: msg}
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(httpStatus)
+       json.NewEncoder(w).Encode(req)
+}
diff --git a/src/dkv/api/utils_test.go b/src/dkv/api/utils_test.go
new file mode 100644 (file)
index 0000000..0bca7c5
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package api
+
+import (
+       "github.com/stretchr/testify/assert"
+       "os"
+       "testing"
+)
+
+func TestReadJSON(t *testing.T) {
+       oldIoutilRead := IoutilRead
+
+       defer func() {
+               IoutilRead = oldIoutilRead
+       }()
+
+       IoutilRead = func(path string) ([]byte, error) {
+               return []byte("test"), nil
+       }
+
+       _, err := ReadJSON("path")
+       assert.Equal(t, nil, err, "Error should be nil.")
+}
+func TestReadJSON_err(t *testing.T) {
+       oldIoutilRead := IoutilRead
+
+       defer func() {
+               IoutilRead = oldIoutilRead
+       }()
+
+       _, err := ReadJSON("path")
+       assert.NotNil(t, err, "Err should not be nil.")
+}
+
+func TestWriteJSON(t *testing.T) {
+       oldReadJson := JsonReader
+       oldIoutilWrite := IoutilWrite
+
+       defer func() {
+               JsonReader = oldReadJson
+               IoutilWrite = oldIoutilWrite
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       err := WriteJSON("path", "token3", "service3")
+       assert.Equal(t, nil, err, "Error should be nil.")
+
+}
+
+func TestDeleteInJSON(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       err := DeleteInJSON("path", "token2")
+       assert.Equal(t, nil, err, "Error should be nil.")
+}
+
+func TestDeleteInJSON_single(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               tsm_list = append(tsm_list, o1)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       err := DeleteInJSON("path", "token1")
+       assert.Equal(t, nil, err, "Error should be nil.")
+}
+
+func TestDeleteInJSON_not_found(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       err := DeleteInJSON("path", "token3")
+       assert.NotNil(t, err, "Err should not be nil.")
+}
+
+func TestFindTokenInJSON(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       found, _ := FindTokenInJSON("path", "token2")
+
+       assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestFindServiceInJSON(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       found, _ := FindServiceInJSON("path", "service2")
+
+       assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestFindServiceInJSON_not_found(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       found, _ := FindServiceInJSON("path", "service3")
+
+       assert.False(t, found, "Token should not be found in JSON.")
+}
+
+func TestGetServicebyToken(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       service, found, _ := GetServicebyToken("path", "token1")
+
+       assert.Equal(t, "service1", service, "Service not found")
+       assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestGetServicebyToken_not_found(t *testing.T) {
+       oldReadJson := JsonReader
+       defer func() {
+               JsonReader = oldReadJson
+       }()
+
+       JsonReader = func(path string) ([]Token_service_map, error) {
+               var tsm_list []Token_service_map
+               o1 := Token_service_map{
+                       Token:   "token1",
+                       Service: "service1",
+               }
+               o2 := Token_service_map{
+                       Token:   "token2",
+                       Service: "service2",
+               }
+               tsm_list = append(tsm_list, o1, o2)
+               return tsm_list, nil
+       }
+
+       IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+               return nil
+       }
+
+       service, found, _ := GetServicebyToken("path", "token3")
+
+       assert.Equal(t, "", service, "Service is found")
+       assert.False(t, found, "Token should be found in JSON.")
+}
index 8b4850f..5bf076f 100644 (file)
@@ -31,10 +31,29 @@ func main() {
                log.Fatal(err)
        }
        router := mux.NewRouter()
-       router.HandleFunc("/loadconfigs", api.HandlePOST).Methods("POST")
-       router.HandleFunc("/getconfig/{key}", api.HandleGET).Methods("GET")
-       router.HandleFunc("/deleteconfig/{key}", api.HandleDELETE).Methods("DELETE")
-       router.HandleFunc("/getconfigs", api.HandleGETS).Methods("GET")
+       // Sevice Registration
+       // Domain CRUD
+       router.HandleFunc("/v1/register", api.HandleServiceCreate).Methods("POST")
+       router.HandleFunc("/v1/register/{token}", api.HandleServiceGet).Methods("GET")
+       router.HandleFunc("/v1/register/{token}", api.HandleServiceDelete).Methods("DELETE")
+       // Subdomain CRUD
+       router.HandleFunc("/v1/register/{token}/subdomain", api.HandleServiceSubdomainCreate).Methods("POST")
+       // router.HandleFunc("/v1/register/{token}/subdomain", api.HandleServiceSubdomainGet).Methods("GET")
+       router.HandleFunc("/v1/register/{token}/subdomain/{subdomain}", api.HandleServiceSubdomainDelete).Methods("DELETE")
+       // Configuration CRUD
+       router.HandleFunc("/v1/config", api.HandleConfigUpload).Methods("POST")
+       router.HandleFunc("/v1/config/{token}/{filename}", api.HandleConfigGet).Methods("GET")
+       router.HandleFunc("/v1/config/{token}/{subdomain}/{filename}", api.HandleConfigGet).Methods("GET")
+       router.HandleFunc("/v1/config/{token}/{filename}", api.HandleConfigDelete).Methods("DELETE")
+       router.HandleFunc("/v1/config/{token}/{subdomain}/{filename}", api.HandleConfigDelete).Methods("DELETE")
+       router.HandleFunc("/v1/config/load", api.HandleConfigLoad).Methods("POST")
+       // Load default configs
+       router.HandleFunc("/v1/config/load-default", api.HandleDefaultConfigLoad).Methods("GET")
+       // Direct Consul queries.
+       router.HandleFunc("/v1/getconfig/{token}/{key}", api.HandleGET).Methods("GET")
+       router.HandleFunc("/v1/deleteconfig/{key}", api.HandleDELETE).Methods("DELETE")
+       router.HandleFunc("/v1/getconfigs", api.HandleGETS).Methods("GET")
+
        loggedRouter := handlers.LoggingHandler(os.Stdout, router)
        log.Println("[INFO] Started Distributed KV Store server.")
        log.Fatal(http.ListenAndServe(":8080", loggedRouter))