diff --git a/cmd/laas/main.go b/cmd/laas/main.go index 67cfa5f..c17ff99 100644 --- a/cmd/laas/main.go +++ b/cmd/laas/main.go @@ -4,19 +4,12 @@ package main import ( - "encoding/json" "flag" - "fmt" - "io/ioutil" "log" "github.com/fossology/LicenseDb/pkg/api" - "github.com/fossology/LicenseDb/pkg/authenticate" + "github.com/fossology/LicenseDb/pkg/db" "github.com/fossology/LicenseDb/pkg/models" - "github.com/fossology/LicenseDb/pkg/utils" - "github.com/gin-gonic/gin" - "gorm.io/driver/postgres" - "gorm.io/gorm" ) // declare flags to input the basic requirement of database connection and the path of the data file @@ -40,46 +33,18 @@ var ( func main() { flag.Parse() - dburi := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s", *dbhost, *port, *user, *dbname, *password) - gormConfig := &gorm.Config{} - database, err := gorm.Open(postgres.Open(dburi), gormConfig) - if err != nil { - log.Fatalf("Failed to connect to database: %v", err) - } + db.Connect(dbhost, port, user, dbname, password) - if err := database.AutoMigrate(&models.LicenseDB{}); err != nil { + if err := db.DB.AutoMigrate(&models.LicenseDB{}); err != nil { log.Fatalf("Failed to automigrate database: %v", err) } - if err := database.AutoMigrate(&models.User{}); err != nil { + if err := db.DB.AutoMigrate(&models.User{}); err != nil { log.Fatalf("Failed to automigrate database: %v", err) } - if *populatedb { - var licenses []models.LicenseJson - // read the file of data - byteResult, _ := ioutil.ReadFile(*datafile) - // unmarshal the json file and it into the struct format - if err := json.Unmarshal(byteResult, &licenses); err != nil { - log.Fatalf("error reading from json file: %v", err) - } - for _, license := range licenses { - // populate the data in the database table - result := utils.Converter(license) - database.Create(&result) - } - } - api.DB = database - r := gin.Default() - r.NoRoute(api.HandleInvalidUrl) - authorized := r.Group("/") - authorized.Use(authenticate.AuthenticationMiddleware()) - authorized.GET("/api/license/:shortname", api.GetLicense) - authorized.POST("/api/license", api.CreateLicense) - authorized.PATCH("/api/license/update/:shortname", api.UpdateLicense) - authorized.GET("/api/licenses", api.SearchInLicense) - r.POST("/api/user", authenticate.CreateUser) - authorized.GET("/api/users", authenticate.GetAllUser) - authorized.GET("/api/user/:id", authenticate.GetUser) + db.Populatedb(*populatedb, *datafile) + + r := api.Router() r.Run() } diff --git a/go.mod b/go.mod index 03f5281..6f32d2f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/gin-gonic/gin v1.9.1 + github.com/stretchr/testify v1.8.3 gorm.io/driver/postgres v1.5.2 gorm.io/gorm v1.25.1 ) @@ -11,6 +12,7 @@ require ( require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -30,6 +32,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect diff --git a/pkg/api/api.go b/pkg/api/api.go index de41d17..db50d88 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -9,12 +9,35 @@ import ( "net/http" "time" + "github.com/fossology/LicenseDb/pkg/authenticate" + "github.com/fossology/LicenseDb/pkg/db" "github.com/fossology/LicenseDb/pkg/models" "github.com/gin-gonic/gin" - "gorm.io/gorm" ) -var DB *gorm.DB +func Router() *gin.Engine { + // r is a default instance of gin engine + r := gin.Default() + + // return error for invalid routes + r.NoRoute(HandleInvalidUrl) + + // authorization not required for these routes + r.GET("/api/license/:shortname", GetLicense) + r.GET("/api/licenses", SearchInLicense) + r.GET("/api/users", authenticate.GetAllUser) + r.GET("/api/user/:id", authenticate.GetUser) + + // set up authentication + authorized := r.Group("/") + authorized.Use(authenticate.AuthenticationMiddleware()) + + authorized.POST("/api/license", CreateLicense) + authorized.PATCH("/api/license/update/:shortname", UpdateLicense) + authorized.POST("/api/user", authenticate.CreateUser) + + return r +} func HandleInvalidUrl(c *gin.Context) { @@ -28,9 +51,10 @@ func HandleInvalidUrl(c *gin.Context) { c.JSON(http.StatusNotFound, er) } func GetAllLicense(c *gin.Context) { + var licenses []models.LicenseDB - err := DB.Find(&licenses).Error + err := db.DB.Find(&licenses).Error if err != nil { er := models.LicenseError{ Status: http.StatusBadRequest, @@ -45,7 +69,7 @@ func GetAllLicense(c *gin.Context) { res := models.LicenseResponse{ Data: licenses, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: len(licenses), }, } @@ -61,7 +85,7 @@ func GetLicense(c *gin.Context) { return } - err := DB.Where("shortname = ?", queryParam).First(&license).Error + err := db.DB.Where("shortname = ?", queryParam).First(&license).Error if err != nil { er := models.LicenseError{ @@ -78,7 +102,7 @@ func GetLicense(c *gin.Context) { res := models.LicenseResponse{ Data: []models.LicenseDB{license}, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -106,7 +130,7 @@ func CreateLicense(c *gin.Context) { } license := models.LicenseDB(input) - result := DB.FirstOrCreate(&license) + result := db.DB.FirstOrCreate(&license) if result.RowsAffected == 0 { er := models.LicenseError{ @@ -133,7 +157,7 @@ func CreateLicense(c *gin.Context) { res := models.LicenseResponse{ Data: []models.LicenseDB{license}, Status: http.StatusCreated, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -145,7 +169,7 @@ func UpdateLicense(c *gin.Context) { var update models.LicenseDB var license models.LicenseDB shortname := c.Param("shortname") - if err := DB.Where("shortname = ?", shortname).First(&license).Error; err != nil { + if err := db.DB.Where("shortname = ?", shortname).First(&license).Error; err != nil { er := models.LicenseError{ Status: http.StatusBadRequest, Message: fmt.Sprintf("license with shortname '%s' not found", shortname), @@ -167,7 +191,7 @@ func UpdateLicense(c *gin.Context) { c.JSON(http.StatusBadRequest, er) return } - if err := DB.Model(&license).Updates(update).Error; err != nil { + if err := db.DB.Model(&license).Updates(update).Error; err != nil { er := models.LicenseError{ Status: http.StatusInternalServerError, Message: "Failed to update license", @@ -181,7 +205,7 @@ func UpdateLicense(c *gin.Context) { res := models.LicenseResponse{ Data: []models.LicenseDB{license}, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -194,16 +218,62 @@ func SearchInLicense(c *gin.Context) { field := c.Query("field") search_term := c.Query("search_term") search := c.Query("search") - if field == "" && search_term == "" { + SpdxId := c.Query("spdxid") + DetectorType := c.Query("detector_type") + GPLv2compatible := c.Query("gplv2compatible") + GPLv3compatible := c.Query("gplv3compatible") + marydone := c.Query("marydone") + active := c.Query("active") + OSIapproved := c.Query("osiapproved") + fsffree := c.Query("fsffree") + copyleft := c.Query("copyleft") + var license []models.LicenseDB + query := db.DB.Model(&license) + + if field == "" && search_term == "" && SpdxId == "" && GPLv2compatible == "" && GPLv3compatible == "" && DetectorType == "" && marydone == "" && active == "" && fsffree == "" && OSIapproved == "" && copyleft == "" { GetAllLicense(c) return } - var query *gorm.DB - var license []models.License + if active != "" { + query = query.Where("active=?", active) + } + + if fsffree != "" { + query = query.Where("fs_ffree=?", fsffree) + } + + if OSIapproved != "" { + query = query.Where("os_iapproved=?", OSIapproved) + } + + if copyleft != "" { + query = query.Where("copyleft=?", copyleft) + } + + if SpdxId != "" { + query = query.Where("spdx_id=?", SpdxId) + } + + if DetectorType != "" { + query = query.Where("detector_type=?", DetectorType) + } + + if GPLv2compatible != "" { + query = query.Where("gp_lv2compatible=?", GPLv2compatible) + } + + if GPLv3compatible != "" { + query = query.Where("gp_lv3compatible=?", GPLv3compatible) + } + + if marydone != "" { + query = query.Where("marydone=?", marydone) + } + if search == "fuzzy" { - query = DB.Where(fmt.Sprintf("%s ILIKE ?", field), fmt.Sprintf("%%%s%%", search_term)).Find(&license) + query = query.Where(fmt.Sprintf("%s ILIKE ?", field), fmt.Sprintf("%%%s%%", search_term)) } else if search == "" || search == "full_text_search" { - query = DB.Where(field+" @@ plainto_tsquery(?)", search_term).Find(&license) + query = query.Where(field+" @@ plainto_tsquery(?)", search_term) } else { err := errors.New("") er := models.LicenseError{ @@ -228,10 +298,12 @@ func SearchInLicense(c *gin.Context) { c.JSON(http.StatusBadRequest, er) return } + query.Find(&license) + res := models.LicenseResponse{ Data: license, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: len(license), }, } diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go new file mode 100644 index 0000000..f9eb8c5 --- /dev/null +++ b/pkg/api/api_test.go @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: 2023 Kavya Shukla +// SPDX-License-Identifier: GPL-2.0-only + +package api + +import ( + "bytes" + "encoding/base64" + "encoding/json" + + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/fossology/LicenseDb/pkg/db" + "github.com/fossology/LicenseDb/pkg/models" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestMain(m *testing.M) { + gin.SetMode(gin.TestMode) + dbname := "fossology" + user := "fossy" + password := "fossy" + port := "5432" + dbhost := "localhost" + db.Connect(&dbhost, &port, &user, &dbname, &password) + + exitcode := m.Run() + os.Exit(exitcode) +} + +func makeRequest(method, path string, body interface{}, isAuthanticated bool) *httptest.ResponseRecorder { + reqBody, _ := json.Marshal(body) + req := httptest.NewRequest(method, path, bytes.NewBuffer(reqBody)) + req.Header.Set("Content-Type", "application/json") + if isAuthanticated { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("fossy:fossy"))) + } + w := httptest.NewRecorder() + Router().ServeHTTP(w, req) + return w +} +func TestGetLicense(t *testing.T) { + expectLicense := models.LicenseDB{ + Shortname: "MIT", + Fullname: "MIT License", + Text: "MIT License\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + Url: "https://opensource.org/licenses/MIT", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + Flag: "1", + Marydone: "f", + SpdxId: "MIT", + } + w := makeRequest("GET", "/api/license/MIT", nil, false) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.LicenseResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, expectLicense, res.Data[0]) + +} + +func TestCreateLicense(t *testing.T) { + License := models.LicenseDB{ + Shortname: "ABCD", + Fullname: "ABCD License", + Text: "just a license", + Url: "https://abcdlicense/ABCD", + SpdxId: "1", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + } + w := makeRequest("POST", "/api/license", License, true) + assert.Equal(t, http.StatusCreated, w.Code) + type response struct { + Data models.LicenseDB `json:"data"` + } + + var res models.LicenseResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, License, res.Data[0]) + +} + +func TestUpdateLicense(t *testing.T) { + License := models.LicenseDB{ + Marydone: "t", + } + expectedLicense := models.LicenseDB{ + Shortname: "MIT", + Fullname: "MIT License", + Text: "MIT License\n\nCopyright (c) \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + Url: "https://opensource.org/licenses/MIT", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + Flag: "1", + Marydone: "t", + SpdxId: "MIT", + } + w := makeRequest("PATCH", "/api/license/update/MIT", License, true) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.LicenseResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, expectedLicense, res.Data[0]) + +} + +func TestSearchInLicense(t *testing.T) { + expectLicense := models.LicenseDB{ + Shortname: "PostgreSQL", + Fullname: "PostgreSQL License", + Text: "PostgreSQL Database Management System\n(formerly known as Postgres, then as Postgres95)\n\nPortions Copyright (c) 1996-2010, The PostgreSQL Global Development Group\n\nPortions Copyright (c) 1994, The Regents of the University of California\n\nPermission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.\n\nIN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nTHE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n", + Url: "http://www.postgresql.org/about/licence", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + Flag: "1", + Marydone: "f", + SpdxId: "PostgreSQL", + } + w := makeRequest("GET", "/api/licenses?field=fullname&search_term=Postgresql", nil, false) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.LicenseResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, expectLicense, res.Data[0]) + +} + +func TestSearchInLicense2(t *testing.T) { + expectLicense := []models.LicenseDB{ + {Shortname: "GPL-2.0-with-autoconf-exception", + Fullname: "GNU General Public License v2.0 w/Autoconf exception", + Text: "insert GPL v2 license text here\n\nAutoconf Exception\n\nAs a special exception, the Free Software Foundation gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of Autoconf appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf program.\n\nCertain portions of the Autoconf source text are designed to be copied (in certain cases, depending on the input) into the output of Autoconf. We call these the \"data\" portions. The rest of the Autoconf source text consists of comments plus executable code that decides which of the data portions to output in any given case. We call these comments and executable code the \"non-data\" portions. Autoconf never copies any of the non-data portions into its output.\n\nThis special exception to the GPL applies to versions of Autoconf released by the Free Software Foundation. When you make and distribute a modified version of Autoconf, you may extend this special exception to the GPL to apply to your modified version as well, *unless* your modified version has the potential to copy into its output some of the text that was the non-data portion of the version that you started with. (In other words, unless your change moves or copies text from the non-data portions to the data portions.) If your modification has such potential, you must delete any notice of this special exception to the GPL from your modified version.\n\n", + Url: "http://ac-archive.sourceforge.net/doc/copyright.html", + Notes: "DEPRECATED: Use license expression including main license, \"WITH\" operator, and identifier: Autoconf-exception-2.0", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + Flag: "1", + Marydone: "f", + SpdxId: "LicenseRef-fossology-GPL-2.0-with-autoconf-exception", + }, + { + Shortname: "Autoconf-exception-2.0", + Fullname: "Autoconf exception 2.0", + Text: "As a special exception, the Free Software Foundation gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of Autoconf appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf program.\n\nCertain portions of the Autoconf source text are designed to be copied (in certain cases, depending on the input) into the output of Autoconf. We call these the \"data\" portions. The rest of the Autoconf source text consists of comments plus executable code that decides which of the data portions to output in any given case. We call these comments and executable code the \"non-data\" portions. Autoconf never copies any of the non-data portions into its output.\n\nThis special exception to the GPL applies to versions of Autoconf released by the Free Software Foundation. When you make and distribute a modified version of Autoconf, you may extend this special exception to the GPL to apply to your modified version as well, *unless* your modified version has the potential to copy into its output some of the text that was the non-data portion of the version that you started with. (In other words, unless your change moves or copies text from the non-data portions to the data portions.) If your modification has such potential, you must delete any notice of this special exception to the GPL from your modified version.\n", + Url: "http://ac-archive.sourceforge.net/doc/copyright.html", + Notes: "Typically used with GPL-2.0", + TextUpdatable: "f", + DetectorType: "1", + Active: "t", + Flag: "1", + Marydone: "f", + SpdxId: "Autoconf-exception-2.0", + }, + } + + w := makeRequest("GET", "/api/licenses?field=url&search_term=http://ac-archive.sourceforge.net/doc/copyright.html", nil, false) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.LicenseResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, expectLicense, res.Data) +} + +func TestGetUser(t *testing.T) { + expectUser := models.User{ + Userid: "1", + Username: "fossy", + Userpassword: "fossy", + Userlevel: "admin", + } + w := makeRequest("GET", "/api/user/1", nil, false) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.UserResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, expectUser, res.Data[0]) + +} + +func TestCreateUser(t *testing.T) { + user := models.User{ + Userid: "2", + Username: "general_user", + Userpassword: "abc123", + Userlevel: "participant", + } + w := makeRequest("POST", "/api/user", user, true) + assert.Equal(t, http.StatusOK, w.Code) + + var res models.UserResponse + json.Unmarshal(w.Body.Bytes(), &res) + + assert.Equal(t, user, res.Data[0]) +} diff --git a/pkg/authenticate/auth.go b/pkg/authenticate/auth.go index 2339e2b..c6df9c9 100644 --- a/pkg/authenticate/auth.go +++ b/pkg/authenticate/auth.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/fossology/LicenseDb/pkg/api" + "github.com/fossology/LicenseDb/pkg/db" "github.com/fossology/LicenseDb/pkg/models" "github.com/gin-gonic/gin" ) @@ -31,7 +31,7 @@ func CreateUser(c *gin.Context) { return } - result := api.DB.Create(&user) + result := db.DB.Create(&user) if result.Error != nil { er := models.LicenseError{ Status: http.StatusInternalServerError, @@ -46,7 +46,7 @@ func CreateUser(c *gin.Context) { res := models.UserResponse{ Data: []models.User{user}, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -56,7 +56,7 @@ func CreateUser(c *gin.Context) { func GetAllUser(c *gin.Context) { var users []models.User - err := api.DB.Find(&users).Error + err := db.DB.Find(&users).Error if err != nil { er := models.LicenseError{ Status: http.StatusBadRequest, @@ -70,7 +70,7 @@ func GetAllUser(c *gin.Context) { res := models.UserResponse{ Data: users, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -81,7 +81,7 @@ func GetAllUser(c *gin.Context) { func GetUser(c *gin.Context) { var user models.User id := c.Param("id") - err := api.DB.Where("id = ?", id).First(&user).Error + err := db.DB.Where("userid = ?", id).First(&user).Error if err != nil { er := models.LicenseError{ Status: http.StatusBadRequest, @@ -95,7 +95,7 @@ func GetUser(c *gin.Context) { res := models.UserResponse{ Data: []models.User{user}, Status: http.StatusOK, - Meta: models.Meta{ + Meta: models.PaginationMeta{ ResourceCount: 1, }, } @@ -124,7 +124,7 @@ func AuthenticationMiddleware() gin.HandlerFunc { decodedAuth, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic ")) if err != nil { er := models.LicenseError{ - Status: http.StatusBadRequest, + Status: http.StatusUnauthorized, Message: fmt.Sprintf("Please check your credentials and try again"), Error: err.Error(), Path: c.Request.URL.Path, @@ -146,7 +146,7 @@ func AuthenticationMiddleware() gin.HandlerFunc { password := auth[1] var user models.User - result := api.DB.Where("username = ?", username).First(&user) + result := db.DB.Where("username = ?", username).First(&user) if result.Error != nil { er := models.LicenseError{ Status: http.StatusUnauthorized, diff --git a/pkg/db/db.go b/pkg/db/db.go new file mode 100644 index 0000000..4ec7f72 --- /dev/null +++ b/pkg/db/db.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 Kavya Shukla +// SPDX-License-Identifier: GPL-2.0-only + +package db + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + + "github.com/fossology/LicenseDb/pkg/models" + "github.com/fossology/LicenseDb/pkg/utils" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func Connect(dbhost, port, user, dbname, password *string) { + + dburi := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s", *dbhost, *port, *user, *dbname, *password) + gormConfig := &gorm.Config{} + database, err := gorm.Open(postgres.Open(dburi), gormConfig) + if err != nil { + log.Fatalf("Failed to connect to database: %v", err) + } + + DB = database +} + +func Populatedb(populatedb bool, datafile string) { + if populatedb { + var licenses []models.LicenseJson + // read the file of data + byteResult, _ := ioutil.ReadFile(datafile) + // unmarshal the json file and it into the struct format + if err := json.Unmarshal(byteResult, &licenses); err != nil { + log.Fatalf("error reading from json file: %v", err) + } + for _, license := range licenses { + // populate the data in the database table + result := utils.Converter(license) + DB.Create(&result) + } + } +} diff --git a/pkg/models/types.go b/pkg/models/types.go index 3b570ca..fc75ece 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -56,7 +56,7 @@ type LicenseJson struct { // license retrieval operation. // It contains information that provides context and supplementary details // about the retrieved license data. -type Meta struct { +type PaginationMeta struct { ResourceCount int `json:"resource_count"` Page int `json:"page,omitempty"` PerPage int `json:"per_page,omitempty"` @@ -67,9 +67,9 @@ type Meta struct { // retrieving license information. // It is used to encapsulate license-related data in an organized manner. type LicenseResponse struct { - Status int `json:"status"` - Data []LicenseDB `json:"data"` - Meta Meta `json:"meta"` + Status int `json:"status"` + Data []LicenseDB `json:"data"` + Meta PaginationMeta `json:"paginationmeta"` } // The LicenseError struct represents an error response related to license operations. @@ -89,7 +89,7 @@ type LicenseInput struct { Shortname string `json:"rf_shortname" binding:"required"` Fullname string `json:"rf_fullname" binding:"required"` Text string `json:"rf_text" binding:"required"` - Url string `json:"rf_url" binding:"required"` + Url string `json:"rf_url" ` AddDate string `json:"rf_add_date"` Copyleft string `json:"rf_copyleft"` FSFfree string `json:"rf_FSFfree"` @@ -110,15 +110,15 @@ type LicenseInput struct { // User struct is representation of user information. type User struct { - Userid string `json:"userid" gorm:"primary_key"` - Username string `json:"username" gorm:"unique"` - Userlevel string `json:"userlevel" gorm:"unique"` - Userpassword string `json:"userpassword" gorm:"unique"` + Userid string `json:"userid" gorm:"primary_key" binding:"required"` + Username string `json:"username" gorm:"unique" binding:"required"` + Userlevel string `json:"userlevel" gorm:"unique" binding:"required"` + Userpassword string `json:"userpassword" gorm:"unique" binding:"required"` } // UserResponse struct is representation of design API response of user. type UserResponse struct { - Status int `json:"status"` - Data []User `json:"data"` - Meta Meta `json:"meta"` + Status int `json:"status"` + Data []User `json:"data"` + Meta PaginationMeta `json:"paginationmeta"` } diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 08b12bf..3f1fadc 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -15,7 +15,7 @@ func Converter(input models.LicenseJson) models.LicenseDB { Shortname: input.Shortname, Fullname: input.Fullname, Text: input.Text, - Url: input.Fullname, + Url: input.Url, Copyleft: input.Copyleft, AddDate: input.AddDate, FSFfree: input.FSFfree,