diff --git a/README.md b/README.md index 18b47a2..f9ad0cf 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ bin/xconfadmin-linux-amd64 -f config/xconfadmin.conf Test the server is running: ```bash -curl http://localhost:9001/api/v1/version +curl http://localhost:9001/api/version ``` Expected response: diff --git a/adminapi/adminapi_common.go b/adminapi/adminapi_common.go index 60fe419..4bb91c7 100644 --- a/adminapi/adminapi_common.go +++ b/adminapi/adminapi_common.go @@ -26,6 +26,7 @@ import ( queries "github.com/rdkcentral/xconfadmin/adminapi/queries" common "github.com/rdkcentral/xconfadmin/common" xhttp "github.com/rdkcentral/xconfadmin/http" + xapptype "github.com/rdkcentral/xconfadmin/shared/applicationtype" log "github.com/sirupsen/logrus" ) @@ -52,11 +53,9 @@ func WebServerInjection(ws *xhttp.WebconfigServer, xc *dataapi.XconfConfigs) { common.WakeupPoolTagName = "t_canary_wakeup" } else { common.AuthProvider = ws.XW_XconfServer.ServerConfig.GetString("xconfwebconfig.xconf.authprovider") - applicationTypeString := ws.XW_XconfServer.ServerConfig.GetString("xconfwebconfig.xconf.application_types") - if applicationTypeString == "" { - applicationTypeString = "stb" + if len(common.ApplicationTypes) == 0 { + common.ApplicationTypes = []string{"stb"} } - common.ApplicationTypes = strings.Split(applicationTypeString, ",") common.CacheUpdateWindowSize = ws.XW_XconfServer.ServerConfig.GetInt64("xconfwebconfig.xconf.cache_update_window_size") common.SatOn = ws.XW_XconfServer.ServerConfig.GetBoolean("xconfwebconfig.sat.SAT_ON") common.AllowedNumberOfFeatures = int(ws.XW_XconfServer.ServerConfig.GetInt32("xconfwebconfig.xconf.allowedNumberOfFeatures", 100)) @@ -115,8 +114,22 @@ func WebServerInjection(ws *xhttp.WebconfigServer, xc *dataapi.XconfConfigs) { func initDB() { queries.CreateFirmwareRuleTemplates() // Initialize FirmwareRule templates initAppSettings() // Initialize Application settings + loadApplicationTypes() // Load Application Types from DB } +func loadApplicationTypes() { + appTypes, err := xapptype.GetAllApplicationTypeAsList() + if err != nil { + log.Errorf("Error loading application types from DB: %v", err) + return + } + var appTypeNames []string + for _, appType := range appTypes { + appTypeNames = append(appTypeNames, appType.Name) + } + common.ApplicationTypes = appTypeNames + log.Info("Loaded application types from DB") +} func initAppSettings() { settings, err := common.GetAppSettings() if err != nil { diff --git a/adminapi/applicationtype/application_type_handler.go b/adminapi/applicationtype/application_type_handler.go new file mode 100644 index 0000000..a4c0fe7 --- /dev/null +++ b/adminapi/applicationtype/application_type_handler.go @@ -0,0 +1,208 @@ +package applicationtype + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/mux" + "github.com/rdkcentral/xconfadmin/adminapi/auth" + xhttp "github.com/rdkcentral/xconfadmin/http" + xapptype "github.com/rdkcentral/xconfadmin/shared/applicationtype" + xwhttp "github.com/rdkcentral/xconfwebconfig/http" +) + +func CreateApplicationTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := auth.CanWrite(r, auth.CHANGE_ENTITY) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, err.Error()) + return + } + var appType xapptype.ApplicationType + xw, ok := w.(*xwhttp.XResponseWriter) + if !ok { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "responsewriter cast error") + return + } + body := xw.Body() + err = json.Unmarshal([]byte(body), &appType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + isDefault := xapptype.IsDefaultAppType(appType.Name) + if isDefault { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Cannot create default application type") + return + } + exists, _ := xapptype.ApplicationTypeNameExists(appType.Name) + if exists { + xhttp.WriteAdminErrorResponse(w, http.StatusConflict, "Application type already exists") + return + } + createdAppType, err := CreateApplicationType(r, &appType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + data, err := json.Marshal(createdAppType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + xhttp.WriteXconfResponse(w, http.StatusCreated, data) +} + +func GetAllApplicationTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := auth.CanRead(r, auth.CHANGE_ENTITY) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, err.Error()) + return + } + appTypes, err := xapptype.GetAllApplicationTypeAsList() + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + data, err := json.Marshal(appTypes) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + xhttp.WriteXconfResponse(w, http.StatusOK, data) +} + +func GetApplicationTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := auth.CanRead(r, auth.CHANGE_ENTITY) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, err.Error()) + return + } + id := mux.Vars(r)["id"] + if id == "" { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Application type ID is required") + return + } + appType, err := xapptype.GetOneApplicationType(id) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + if appType == nil { + xhttp.WriteAdminErrorResponse(w, http.StatusNotFound, "Application type not found") + return + } + data, err := json.Marshal(appType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + xhttp.WriteXconfResponse(w, http.StatusOK, data) +} + +func DeleteApplicationTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := auth.CanWrite(r, auth.CHANGE_ENTITY) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, err.Error()) + return + } + id := mux.Vars(r)["id"] + if id == "" { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Application type ID is required") + return + } + appType, err := xapptype.GetOneApplicationType(id) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + if appType == nil { + xhttp.WriteAdminErrorResponse(w, http.StatusNotFound, "Application type not found") + return + } + if xapptype.IsDefaultAppType(appType.Name) { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Default application types cannot be deleted") + return + } + err = xapptype.DeleteOneApplicationType(id) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + response := map[string]string{ + "message": fmt.Sprintf("Application type '%s' deleted successfully", appType.Name), + } + data, err := json.Marshal(response) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + xhttp.WriteXconfResponse(w, http.StatusOK, data) +} + +func UpdateApplicationTypeHandler(w http.ResponseWriter, r *http.Request) { + _, err := auth.CanWrite(r, auth.CHANGE_ENTITY) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, err.Error()) + return + } + id := mux.Vars(r)["id"] + if id == "" { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Application type ID is required") + return + } + xw, ok := w.(*xwhttp.XResponseWriter) + if !ok { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "responsewriter cast error") + return + } + body := xw.Body() + var updateRequest xapptype.ApplicationType + err = json.Unmarshal([]byte(body), &updateRequest) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + err = ValidateApplicationType(&updateRequest) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + existingAppType, err := xapptype.GetOneApplicationType(id) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + if existingAppType.Name != updateRequest.Name { + nameExists, _ := xapptype.ApplicationTypeNameExists(updateRequest.Name) + if nameExists { + xhttp.WriteAdminErrorResponse(w, http.StatusConflict, + fmt.Sprintf("Application type with name '%s' already exists", updateRequest.Name)) + return + } + } + if xapptype.IsDefaultAppType(existingAppType.Name) { + xhttp.WriteAdminErrorResponse(w, http.StatusBadRequest, "Default application types cannot be updated") + return + } + existingAppType.ID = id + existingAppType.Name = updateRequest.Name + if updateRequest.Description != "" { + existingAppType.Description = updateRequest.Description + } + existingAppType.UpdatedAt = time.Now().Unix() + + err = xapptype.SetOneApplicationType(existingAppType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + data, err := json.Marshal(existingAppType) + if err != nil { + xhttp.WriteAdminErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + xhttp.WriteXconfResponse(w, http.StatusOK, data) +} diff --git a/adminapi/applicationtype/application_type_handler_test.go b/adminapi/applicationtype/application_type_handler_test.go new file mode 100644 index 0000000..f025abd --- /dev/null +++ b/adminapi/applicationtype/application_type_handler_test.go @@ -0,0 +1,109 @@ +package applicationtype + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + xwhttp "github.com/rdkcentral/xconfwebconfig/http" + "github.com/stretchr/testify/assert" +) + +func TestCreateApplicationTypeHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/api/application-types", nil) + rec := httptest.NewRecorder() + + CreateApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + w := xwhttp.NewXResponseWriter(rec) + + invalidJson := `{invalid json}` + w.SetBody(invalidJson) + CreateApplicationTypeHandler(w, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + + // valid Json + validJson := `{"name": "testAppType"}` + w.SetBody(validJson) + CreateApplicationTypeHandler(w, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + + w.SetBody(validJson) + CreateApplicationTypeHandler(w, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + + validJson = `{"name": "stb"}` + w.SetBody(validJson) + CreateApplicationTypeHandler(w, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) +} + +func TestGetAllApplicationTypeHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/api/application-types", nil) + rec := httptest.NewRecorder() + GetAllApplicationTypeHandler(rec, req) + assert.True(t, rec.Code == http.StatusOK || rec.Code == http.StatusInternalServerError) +} + +func TestGetApplicationTypeHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/api/application-types/{id}", nil) + req = mux.SetURLVars(req, map[string]string{"id": "nonexistentID"}) + rec := httptest.NewRecorder() + GetApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusInternalServerError, rec.Code) + + req = mux.SetURLVars(req, map[string]string{"id": ""}) + rec = httptest.NewRecorder() + GetApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) +} + +func TestUpdateApplicationTypeHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodPut, "/api/application-types/123", nil) + req = mux.SetURLVars(req, map[string]string{"id": "123"}) + rec := httptest.NewRecorder() + + UpdateApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + assert.Contains(t, rec.Body.String(), "responsewriter cast error") + + rec = httptest.NewRecorder() + w := xwhttp.NewXResponseWriter(rec) + invalidJson := `{invalid json}` + w.SetBody(invalidJson) + UpdateApplicationTypeHandler(w, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + + rec = httptest.NewRecorder() + w = xwhttp.NewXResponseWriter(rec) + validJson := `{"name": "updatedApp"}` + w.SetBody(validJson) + UpdateApplicationTypeHandler(w, req) + assert.True(t, rec.Code == http.StatusOK || rec.Code == http.StatusBadRequest || rec.Code == http.StatusInternalServerError) + + rec = httptest.NewRecorder() + w = xwhttp.NewXResponseWriter(rec) + validJson = `{"name": "updatedAppType"}` + w.SetBody(validJson) + UpdateApplicationTypeHandler(w, req) + assert.True(t, rec.Code == http.StatusOK || rec.Code == http.StatusBadRequest || rec.Code == http.StatusInternalServerError) + + req = mux.SetURLVars(req, map[string]string{"id": ""}) + rec = httptest.NewRecorder() + UpdateApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) +} + +func TestDeleteApplicationTypeHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodDelete, "/api/application-types/{id}", nil) + req = mux.SetURLVars(req, map[string]string{"id": "nonexistentID"}) + rec := httptest.NewRecorder() + DeleteApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusInternalServerError, rec.Code) + + req = mux.SetURLVars(req, map[string]string{"id": ""}) + rec = httptest.NewRecorder() + DeleteApplicationTypeHandler(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) +} diff --git a/adminapi/applicationtype/application_type_service.go b/adminapi/applicationtype/application_type_service.go new file mode 100644 index 0000000..f461174 --- /dev/null +++ b/adminapi/applicationtype/application_type_service.go @@ -0,0 +1,57 @@ +package applicationtype + +import ( + "fmt" + "net/http" + "regexp" + "time" + + "github.com/google/uuid" + "github.com/rdkcentral/xconfadmin/adminapi/auth" + xapptype "github.com/rdkcentral/xconfadmin/shared/applicationtype" + xwcommon "github.com/rdkcentral/xconfwebconfig/common" + log "github.com/sirupsen/logrus" +) + +const ( + applicationRegexPattern = "^[a-zA-Z]{3,12}$" +) + +func CreateApplicationType(r *http.Request, appType *xapptype.ApplicationType) (*xapptype.ApplicationType, error) { + _, err := auth.CanWrite(r, auth.CHANGE_ENTITY) + if err != nil { + return nil, err + } + + if err := ValidateApplicationType(appType); err != nil { + return nil, err + } + + appType.ID = uuid.New().String() + appType.CreatedBy = auth.GetUserNameOrUnknown(r) + appType.CreatedAt = time.Now().Unix() + + err = xapptype.SetOneApplicationType(appType) + if err != nil { + log.Error(fmt.Sprintf("CreateApplicationType error: %v", err)) + return nil, xwcommon.NewRemoteErrorAS(http.StatusInternalServerError, err.Error()) + } + + log.Info(fmt.Sprintf("Application type created: %s by %s", appType.Name, appType.CreatedBy)) + return appType, nil +} + +func ValidateApplicationType(appType *xapptype.ApplicationType) error { + if appType == nil { + return xwcommon.NewRemoteErrorAS(http.StatusBadRequest, "Application type cannot be nil") + } + if appType.Name == "" { + return xwcommon.NewRemoteErrorAS(http.StatusBadRequest, "Application type name cannot be empty") + } + + matched, _ := regexp.MatchString(applicationRegexPattern, appType.Name) + if !matched { + return xwcommon.NewRemoteErrorAS(http.StatusBadRequest, "Application type name must be 3-12 alphabetic characters") + } + return nil +} diff --git a/adminapi/applicationtype/application_type_service_test.go b/adminapi/applicationtype/application_type_service_test.go new file mode 100644 index 0000000..e653318 --- /dev/null +++ b/adminapi/applicationtype/application_type_service_test.go @@ -0,0 +1,35 @@ +package applicationtype + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + xapptype "github.com/rdkcentral/xconfadmin/shared/applicationtype" + "github.com/stretchr/testify/assert" +) + +func TestCreateApplicationType(t *testing.T) { + appType := &xapptype.ApplicationType{ + Name: "testApp", + } + req := httptest.NewRequest(http.MethodPost, "/api/application-types", nil) + req.Header.Set("X-User-Name", "testuser") + createdAppType, err := CreateApplicationType(req, appType) + + if err == nil || (err != nil && !strings.Contains(err.Error(), "cache not found") && !strings.Contains(err.Error(), "Table configuration not found")) { + assert.NotNil(t, createdAppType) + assert.NoError(t, err) + } else { + t.Skip("Skipping: database not configured") + } +} + +func TestValidateApplicationType(t *testing.T) { + err := ValidateApplicationType(nil) + assert.Error(t, err) + + err = ValidateApplicationType(&xapptype.ApplicationType{Name: ""}) + assert.Error(t, err) +} diff --git a/adminapi/router.go b/adminapi/router.go index 3c82ae7..573bb35 100644 --- a/adminapi/router.go +++ b/adminapi/router.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/gorilla/mux" + "github.com/rdkcentral/xconfadmin/adminapi/applicationtype" "github.com/rdkcentral/xconfadmin/adminapi/auth" "github.com/rdkcentral/xconfadmin/adminapi/canary" "github.com/rdkcentral/xconfadmin/adminapi/change" @@ -113,6 +114,15 @@ func RouteXconfAdminserviceApis(s *xhttp.WebconfigServer, r *mux.Router) { urlPath.HandleFunc("", auth.LoginUrlHandler).Methods("GET").Name("Auth-Xerxes") authPaths = append(authPaths, urlPath) + //Application Type APIs + applicationTypePath := r.PathPrefix("/xconfAdminService/application-types").Subrouter() + applicationTypePath.HandleFunc("", applicationtype.CreateApplicationTypeHandler).Methods("POST").Name("Application-Type") + applicationTypePath.HandleFunc("", applicationtype.GetAllApplicationTypeHandler).Methods("GET").Name("Application-Type") + applicationTypePath.HandleFunc("/{id}", applicationtype.GetApplicationTypeHandler).Methods("GET").Name("Application-Type") + applicationTypePath.HandleFunc("/{id}", applicationtype.DeleteApplicationTypeHandler).Methods("DELETE").Name("Application-Type") + applicationTypePath.HandleFunc("/{id}", applicationtype.UpdateApplicationTypeHandler).Methods("PUT").Name("Application-Type") + paths = append(paths, applicationTypePath) + // DataService bypass APIs dsBypassPathPrefix := r.PathPrefix("/xconfAdminService/dataService").Subrouter() dsBypassPathPrefix.HandleFunc("/xconf/swu/{applicationType}", dataapi.GetEstbFirmwareSwuHandler).Methods("GET").Name("DataServiceByPass") diff --git a/go.mod b/go.mod index 09ec494..a81bac5 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 - github.com/rdkcentral/xconfwebconfig v0.0.0-20260121220436-4e292e8287c9 + github.com/rdkcentral/xconfwebconfig v0.0.0-20260105061036-e75e3af8c1a3 github.com/sirupsen/logrus v1.9.3 github.com/xeipuuv/gojsonschema v1.2.0 go.uber.org/automaxprocs v1.5.3 diff --git a/go.sum b/go.sum index cde09d4..62f540f 100644 --- a/go.sum +++ b/go.sum @@ -98,14 +98,8 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rdkcentral/xconfwebconfig v0.0.0-20251110232019-da9146c7aedc h1:jBmY1q/0pZIKfr5MivlW4g/JT4ZUXGzQKBnih9sJTps= -github.com/rdkcentral/xconfwebconfig v0.0.0-20251110232019-da9146c7aedc/go.mod h1:e6kwCd+4ru5tDy190JGGcNtc6LyOSdmVaHDRbOcX+5Y= -github.com/rdkcentral/xconfwebconfig v0.0.0-20260108202333-0ac91b0b1ebd h1:Yj6JfJ+2EqAQciDsbTKwR6OK2FaGMtAj76rpRqrimr0= -github.com/rdkcentral/xconfwebconfig v0.0.0-20260108202333-0ac91b0b1ebd/go.mod h1:GU7c3D0c5KEEj/F+1/5O3GUdVIbK5JSlYpFrMKMOZDQ= -github.com/rdkcentral/xconfwebconfig v0.0.0-20260121220436-4e292e8287c9 h1:iyK5HHU3H+CNUrkJprsCvqZ7KFKcO6vMo4F0fJcl3rA= -github.com/rdkcentral/xconfwebconfig v0.0.0-20260121220436-4e292e8287c9/go.mod h1:GU7c3D0c5KEEj/F+1/5O3GUdVIbK5JSlYpFrMKMOZDQ= -github.com/rdkcentral/xconfwebconfig v1.0.15 h1:q0G+CMf5ie6f3xrThh6i6a+j4c3f3e9+ibWD1aXlBY8= -github.com/rdkcentral/xconfwebconfig v1.0.15/go.mod h1:e6kwCd+4ru5tDy190JGGcNtc6LyOSdmVaHDRbOcX+5Y= +github.com/rdkcentral/xconfwebconfig v0.0.0-20260105061036-e75e3af8c1a3 h1:dzXvBOAUEtstyYxPNYZHgfnlaX4TU9KCQ9h9maBj1jA= +github.com/rdkcentral/xconfwebconfig v0.0.0-20260105061036-e75e3af8c1a3/go.mod h1:GU7c3D0c5KEEj/F+1/5O3GUdVIbK5JSlYpFrMKMOZDQ= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= @@ -113,6 +107,8 @@ github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/shared/applicationtype/application_type.go b/shared/applicationtype/application_type.go new file mode 100644 index 0000000..3edcf8c --- /dev/null +++ b/shared/applicationtype/application_type.go @@ -0,0 +1,23 @@ +package applicationtype + +import "strings" + +type ApplicationType struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + CreatedBy string `json:"createdBy"` + CreatedAt int64 `json:"createdAt"` + UpdatedAt int64 `json:"updatedAt,omitempty"` +} + +var defaultTypes = []string{"stb", "xhome", "sky"} + +func IsDefaultAppType(name string) bool { + for _, dt := range defaultTypes { + if strings.EqualFold(name, dt) { + return true + } + } + return false +} diff --git a/shared/applicationtype/application_type_dao.go b/shared/applicationtype/application_type_dao.go new file mode 100644 index 0000000..9e052fb --- /dev/null +++ b/shared/applicationtype/application_type_dao.go @@ -0,0 +1,86 @@ +package applicationtype + +import ( + "encoding/json" + "fmt" + "strings" + + db "github.com/rdkcentral/xconfwebconfig/db" +) + +const ( + TABLE_APPLICATION_TYPES = "ApplicationTypes" +) + +// Get one application type +func GetOneApplicationType(id string) (*ApplicationType, error) { + result, err := db.GetCachedSimpleDao().GetOne(TABLE_APPLICATION_TYPES, id) + if err != nil { + return nil, err + } + if result == nil { + return nil, fmt.Errorf("application type with id %s not found", id) + } + appType, err := toApplicationType(result) + if err != nil { + return nil, err + } + return appType, nil +} + +func SetOneApplicationType(appType *ApplicationType) error { + if err := DeleteOneApplicationType(appType.ID); err != nil { + return err + } + return db.GetCachedSimpleDao().SetOne(TABLE_APPLICATION_TYPES, appType.ID, appType) +} + +func DeleteOneApplicationType(id string) error { + return db.GetCachedSimpleDao().DeleteOne(TABLE_APPLICATION_TYPES, id) +} + +func ApplicationTypeNameExists(name string) (bool, error) { + appTypes, err := GetAllApplicationTypeAsList() + if err != nil { + return false, err + } + + for _, appType := range appTypes { + if strings.EqualFold(appType.Name, name) { + return true, nil + } + } + return false, nil +} + +// Get all application types as list +func GetAllApplicationTypeAsList() ([]*ApplicationType, error) { + appTypeMap, err := db.GetCachedSimpleDao().GetAllAsMap(TABLE_APPLICATION_TYPES) + if err != nil { + return nil, err + } + + var appTypeList []*ApplicationType + for _, v := range appTypeMap { + appType, err := toApplicationType(v) + if err != nil { + return nil, err + } + appTypeList = append(appTypeList, appType) + } + return appTypeList, nil +} + +func toApplicationType(v interface{}) (*ApplicationType, error) { + if appType, ok := v.(*ApplicationType); ok { + return appType, nil + } + + jsonBytes, err := json.Marshal(v) + if err != nil { + return nil, err + } + + appType := &ApplicationType{} + return appType, json.Unmarshal(jsonBytes, appType) +} diff --git a/shared/applicationtype/application_type_dao_test.go b/shared/applicationtype/application_type_dao_test.go new file mode 100644 index 0000000..76d8e4d --- /dev/null +++ b/shared/applicationtype/application_type_dao_test.go @@ -0,0 +1,81 @@ +package applicationtype + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetOneApplicationType(t *testing.T) { + appType := &ApplicationType{ + ID: "testAppTypeID", + Name: "testAppType", + } + err := SetOneApplicationType(appType) + if err != nil && (strings.Contains(err.Error(), "Table configuration not found") || + strings.Contains(err.Error(), "cache not found or configured")) { + t.Skip("Skipping test: database not configured") + return + } + assert.NoError(t, err) +} + +func TestToApplicationType(t *testing.T) { + appType := &ApplicationType{ + ID: "test123", + Name: "testApp", + } + result, err := toApplicationType(appType) + assert.NoError(t, err) + assert.Equal(t, "test123", result.ID) + assert.Equal(t, "testApp", result.Name) + assert.Equal(t, appType, result) + + inputMap := map[string]interface{}{ + "id": "test456", + "name": "testApp2", + } + result, err = toApplicationType(inputMap) + assert.NoError(t, err) + assert.Equal(t, "test456", result.ID) + assert.Equal(t, "testApp2", result.Name) + + invalidInput := make(chan int) + result, err = toApplicationType(invalidInput) + assert.Error(t, err) + assert.Nil(t, result) +} + +func TestGetOneApplicationType(t *testing.T) { + result, err := GetOneApplicationType("7a3rf34ff1d9fa9qa") + if err != nil && (strings.Contains(err.Error(), "Table configuration not found") || + strings.Contains(err.Error(), "cache not found or configured")) { + t.Skip("Skipping test: database not configured") + return + } + assert.NoError(t, err) + assert.Nil(t, result) +} + +func TestApplicationTypeNameExists(t *testing.T) { + exists, err := ApplicationTypeNameExists("test") + if err != nil && (strings.Contains(err.Error(), "Table configuration not found") || + strings.Contains(err.Error(), "cache not found or configured")) { + t.Skip("Skipping test: database not configured") + return + } + assert.NoError(t, err) + assert.False(t, exists) +} + +func TestGetAllApplicationTypeAsList(t *testing.T) { + result, err := GetAllApplicationTypeAsList() + if err != nil && (strings.Contains(err.Error(), "Table configuration not found") || + strings.Contains(err.Error(), "cache not found or configured")) { + t.Skip("Skipping test: database not configured") + return + } + assert.NoError(t, err) + assert.IsType(t, []*ApplicationType{}, result) +} diff --git a/shared/applicationtype/application_type_test.go b/shared/applicationtype/application_type_test.go new file mode 100644 index 0000000..89352f8 --- /dev/null +++ b/shared/applicationtype/application_type_test.go @@ -0,0 +1,12 @@ +package applicationtype + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_IsDefaultAppType(t *testing.T) { + assert.True(t, IsDefaultAppType("stb")) + assert.False(t, IsDefaultAppType("customApp")) +}