Add list api for profiles 08/91708/1
authorKiran Kamineni <kiran.k.kamineni@intel.com>
Thu, 18 Jul 2019 21:49:25 +0000 (14:49 -0700)
committerKiran Kamineni <kiran.k.kamineni@intel.com>
Fri, 19 Jul 2019 00:14:18 +0000 (17:14 -0700)
Add a list api for profiles for a specific
definition and version.
GET /v1/rb/definition/name/version/profile
will list all the profiles.

Issue-ID: MULTICLOUD-730
Change-Id: If1b8e6910c276a0f7139ab13340721c6ec8a49e8
Signed-off-by: Kiran Kamineni <kiran.k.kamineni@intel.com>
src/k8splugin/api/api.go
src/k8splugin/api/profilehandler.go
src/k8splugin/api/profilehandler_test.go
src/k8splugin/internal/rb/profile.go
src/k8splugin/internal/rb/profile_test.go

index 353972a..a6cdfc7 100644 (file)
@@ -78,6 +78,7 @@ func NewRouter(defClient rb.DefinitionManager,
        }
        profileHandler := rbProfileHandler{client: profileClient}
        resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile", profileHandler.createHandler).Methods("POST")
+       resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile", profileHandler.listHandler).Methods("GET")
        resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}/content", profileHandler.uploadHandler).Methods("POST")
        resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}", profileHandler.getHandler).Methods("GET")
        resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}", profileHandler.deleteHandler).Methods("DELETE")
index adb9249..68ab77a 100644 (file)
@@ -20,9 +20,10 @@ import (
        "encoding/json"
        "io"
        "io/ioutil"
-       "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
        "net/http"
 
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
+
        "github.com/gorilla/mux"
 )
 
@@ -119,6 +120,28 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
+// getHandler handles GET operations on a particular ids
+// Returns a rb.Definition
+func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       rbName := vars["rbname"]
+       rbVersion := vars["rbversion"]
+
+       ret, err := h.client.List(rbName, rbVersion)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(ret)
+       if err != nil {
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
 // deleteHandler handles DELETE operations on a particular bundle definition id
 func (h rbProfileHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
index e81fb26..4dae377 100644 (file)
@@ -23,6 +23,7 @@ import (
        "net/http"
        "net/http/httptest"
        "reflect"
+       "sort"
        "testing"
 
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
@@ -57,6 +58,14 @@ func (m *mockRBProfile) Get(rbname, rbversion, prname string) (rb.Profile, error
        return m.Items[0], nil
 }
 
+func (m *mockRBProfile) List(rbname, rbversion string) ([]rb.Profile, error) {
+       if m.Err != nil {
+               return []rb.Profile{}, m.Err
+       }
+
+       return m.Items, nil
+}
+
 func (m *mockRBProfile) Delete(rbname, rbversion, prname string) error {
        return m.Err
 }
@@ -210,6 +219,98 @@ func TestRBProfileGetHandler(t *testing.T) {
        }
 }
 
+func TestRBProfileListHandler(t *testing.T) {
+
+       testCases := []struct {
+               def          string
+               version      string
+               label        string
+               expected     []rb.Profile
+               expectedCode int
+               rbProClient  *mockRBProfile
+       }{
+               {
+                       def:          "test-rbdef",
+                       version:      "v1",
+                       label:        "List Profiles",
+                       expectedCode: http.StatusOK,
+                       expected: []rb.Profile{
+                               {
+                                       RBName:            "test-rbdef",
+                                       RBVersion:         "v1",
+                                       ProfileName:       "profile1",
+                                       ReleaseName:       "testprofilereleasename",
+                                       Namespace:         "ns1",
+                                       KubernetesVersion: "1.12.3",
+                               },
+                               {
+                                       RBName:            "test-rbdef",
+                                       RBVersion:         "v1",
+                                       ProfileName:       "profile2",
+                                       ReleaseName:       "testprofilereleasename",
+                                       Namespace:         "ns2",
+                                       KubernetesVersion: "1.12.3",
+                               },
+                       },
+                       rbProClient: &mockRBProfile{
+                               // list of Profiles that will be returned by the mockclient
+                               Items: []rb.Profile{
+                                       {
+                                               RBName:            "test-rbdef",
+                                               RBVersion:         "v1",
+                                               ProfileName:       "profile1",
+                                               ReleaseName:       "testprofilereleasename",
+                                               Namespace:         "ns1",
+                                               KubernetesVersion: "1.12.3",
+                                       },
+                                       {
+                                               RBName:            "test-rbdef",
+                                               RBVersion:         "v1",
+                                               ProfileName:       "profile2",
+                                               ReleaseName:       "testprofilereleasename",
+                                               Namespace:         "ns2",
+                                               KubernetesVersion: "1.12.3",
+                                       },
+                               },
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       request := httptest.NewRequest("GET", "/v1/rb/definition/"+testCase.def+"/"+testCase.version+"/profile", nil)
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+
+                       //Check returned code
+                       if resp.StatusCode != testCase.expectedCode {
+                               t.Fatalf("Expected %d; Got: %d", testCase.expectedCode, resp.StatusCode)
+                       }
+
+                       //Check returned body only if statusOK
+                       if resp.StatusCode == http.StatusOK {
+                               got := []rb.Profile{}
+                               json.NewDecoder(resp.Body).Decode(&got)
+
+                               // Since the order of returned slice is not guaranteed
+                               // Check both and return error if both don't match
+                               sort.Slice(got, func(i, j int) bool {
+                                       return got[i].ProfileName < got[j].ProfileName
+                               })
+                               // Sort both as it is not expected that testCase.expected
+                               // is sorted
+                               sort.Slice(testCase.expected, func(i, j int) bool {
+                                       return testCase.expected[i].ProfileName < testCase.expected[j].ProfileName
+                               })
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("listHandler returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
 func TestRBProfileDeleteHandler(t *testing.T) {
 
        testCases := []struct {
index 64449eb..49768d4 100644 (file)
@@ -20,6 +20,7 @@ import (
        "bytes"
        "encoding/base64"
        "encoding/json"
+       "log"
        "path/filepath"
 
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
@@ -44,6 +45,7 @@ type Profile struct {
 type ProfileManager interface {
        Create(def Profile) (Profile, error)
        Get(rbName, rbVersion, prName string) (Profile, error)
+       List(rbName, rbVersion string) ([]Profile, error)
        Delete(rbName, rbVersion, prName string) error
        Upload(rbName, rbVersion, prName string, inp []byte) error
 }
@@ -148,6 +150,38 @@ func (v *ProfileClient) Get(rbName, rbVersion, prName string) (Profile, error) {
        return Profile{}, pkgerrors.New("Error getting Resource Bundle Profile")
 }
 
+// List returns the Resource Bundle Profile for corresponding ID
+func (v *ProfileClient) List(rbName, rbVersion string) ([]Profile, error) {
+
+       //Get all profiles
+       dbres, err := db.DBconn.ReadAll(v.storeName, v.tagMeta)
+       if err != nil || len(dbres) == 0 {
+               return []Profile{}, pkgerrors.Wrap(err, "No Profiles Found")
+       }
+
+       var results []Profile
+       for key, value := range dbres {
+               //value is a byte array
+               if value != nil {
+                       pr := Profile{}
+                       err = db.DBconn.Unmarshal(value, &pr)
+                       if err != nil {
+                               log.Printf("[Profile] Error: %s Unmarshaling value for: %s", err.Error(), key)
+                               continue
+                       }
+                       if pr.RBName == rbName && pr.RBVersion == rbVersion {
+                               results = append(results, pr)
+                       }
+               }
+       }
+
+       if len(results) == 0 {
+               return results, pkgerrors.New("No Profiles Found for Definition and Version")
+       }
+
+       return results, nil
+}
+
 // Delete the Resource Bundle Profile from database
 func (v *ProfileClient) Delete(rbName, rbVersion, prName string) error {
        key := ProfileKey{
index 263c48a..26b0161 100644 (file)
@@ -18,11 +18,13 @@ package rb
 
 import (
        "bytes"
-       "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
        "reflect"
+       "sort"
        "strings"
        "testing"
 
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
+
        pkgerrors "github.com/pkg/errors"
 )
 
@@ -187,6 +189,106 @@ func TestGetProfile(t *testing.T) {
        }
 }
 
+func TestListProfile(t *testing.T) {
+
+       testCases := []struct {
+               label         string
+               name          string
+               rbdef         string
+               version       string
+               expectedError string
+               mockdb        *db.MockDB
+               expected      []Profile
+       }{
+               {
+                       label:   "List Resource Bundle Profile",
+                       name:    "testresourcebundle",
+                       rbdef:   "testresourcebundle",
+                       version: "v1",
+                       expected: []Profile{
+                               {
+                                       ProfileName:       "testprofile1",
+                                       ReleaseName:       "testprofilereleasename",
+                                       Namespace:         "testnamespace",
+                                       KubernetesVersion: "1.12.3",
+                                       RBName:            "testresourcebundle",
+                                       RBVersion:         "v1",
+                               },
+                               {
+                                       ProfileName:       "testprofile2",
+                                       ReleaseName:       "testprofilereleasename2",
+                                       Namespace:         "testnamespace2",
+                                       KubernetesVersion: "1.12.3",
+                                       RBName:            "testresourcebundle",
+                                       RBVersion:         "v1",
+                               },
+                       },
+                       expectedError: "",
+                       mockdb: &db.MockDB{
+                               Items: map[string]map[string][]byte{
+                                       ProfileKey{RBName: "testresourcebundle", RBVersion: "v1", ProfileName: "testprofile1"}.String(): {
+                                               "profilemetadata": []byte(
+                                                       "{\"profile-name\":\"testprofile1\"," +
+                                                               "\"release-name\":\"testprofilereleasename\"," +
+                                                               "\"namespace\":\"testnamespace\"," +
+                                                               "\"rb-name\":\"testresourcebundle\"," +
+                                                               "\"rb-version\":\"v1\"," +
+                                                               "\"kubernetes-version\":\"1.12.3\"}"),
+                                       },
+                                       ProfileKey{RBName: "testresourcebundle", RBVersion: "v1", ProfileName: "testprofile2"}.String(): {
+                                               "profilemetadata": []byte(
+                                                       "{\"profile-name\":\"testprofile2\"," +
+                                                               "\"release-name\":\"testprofilereleasename2\"," +
+                                                               "\"namespace\":\"testnamespace2\"," +
+                                                               "\"rb-name\":\"testresourcebundle\"," +
+                                                               "\"rb-version\":\"v1\"," +
+                                                               "\"kubernetes-version\":\"1.12.3\"}"),
+                                       },
+                               },
+                       },
+               },
+               {
+                       label:         "List Error",
+                       expectedError: "DB Error",
+                       mockdb: &db.MockDB{
+                               Err: pkgerrors.New("DB Error"),
+                       },
+               },
+       }
+
+       for _, testCase := range testCases {
+               t.Run(testCase.label, func(t *testing.T) {
+                       db.DBconn = testCase.mockdb
+                       impl := NewProfileClient()
+                       got, err := impl.List(testCase.rbdef, testCase.version)
+                       if err != nil {
+                               if testCase.expectedError == "" {
+                                       t.Fatalf("List returned an unexpected error %s", err)
+                               }
+                               if strings.Contains(err.Error(), testCase.expectedError) == false {
+                                       t.Fatalf("List returned an unexpected error %s", err)
+                               }
+                       } else {
+                               // Since the order of returned slice is not guaranteed
+                               // Check both and return error if both don't match
+                               sort.Slice(got, func(i, j int) bool {
+                                       return got[i].ProfileName < got[j].ProfileName
+                               })
+                               // Sort both as it is not expected that testCase.expected
+                               // is sorted
+                               sort.Slice(testCase.expected, func(i, j int) bool {
+                                       return testCase.expected[i].ProfileName < testCase.expected[j].ProfileName
+                               })
+
+                               if reflect.DeepEqual(testCase.expected, got) == false {
+                                       t.Errorf("List Resource Bundle returned unexpected body: got %v;"+
+                                               " expected %v", got, testCase.expected)
+                               }
+                       }
+               })
+       }
+}
+
 func TestDeleteProfile(t *testing.T) {
 
        testCases := []struct {