diff --git a/README.md b/README.md index d2cf99d..d2ecd0c 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ psql -h localhost -p 5432 -U fossy -d fossology Run the following query to create the first user. ```sql -INSERT INTO users (username, userpassword, userlevel) VALUES ('', '', 'admin'); +INSERT INTO users (username, userpassword, userlevel, display_name, user_email) VALUES ('', '', 'SUPER_ADMIN', '', ''); ``` ### Generating Swagger Documentation diff --git a/cmd/laas/docs/docs.go b/cmd/laas/docs/docs.go index 212d154..6740470 100644 --- a/cmd/laas/docs/docs.go +++ b/cmd/laas/docs/docs.go @@ -683,75 +683,6 @@ const docTemplate = `{ } } } - }, - "patch": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Update a license in the service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Licenses" - ], - "summary": "Update a license", - "operationId": "UpdateLicense", - "parameters": [ - { - "type": "string", - "description": "Shortname of the license to be updated", - "name": "shortname", - "in": "path", - "required": true - }, - { - "description": "Update license body (requires only the fields to be updated)", - "name": "license", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.LicenseUpdateJSONSchema" - } - } - ], - "responses": { - "200": { - "description": "License updated successfully", - "schema": { - "$ref": "#/definitions/models.LicenseResponse" - } - }, - "400": { - "description": "Invalid license body", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "404": { - "description": "License with shortname not found", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "409": { - "description": "License with same shortname already exists", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "500": { - "description": "Failed to update license", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - } - } } }, "/login": { @@ -2500,10 +2431,6 @@ const docTemplate = `{ "models.LicenseId": { "type": "object", "properties": { - "id": { - "type": "integer", - "example": 31 - }, "shortname": { "type": "string", "example": "MIT" @@ -2598,98 +2525,6 @@ const docTemplate = `{ } } }, - "models.LicenseUpdateJSONSchema": { - "type": "object", - "properties": { - "FSFfree": { - "type": "boolean", - "example": false - }, - "Fedora": { - "type": "string", - "example": "Fedora" - }, - "GPLv2compatible": { - "type": "boolean", - "example": false - }, - "GPLv3compatible": { - "type": "boolean", - "example": false - }, - "OSIapproved": { - "type": "boolean", - "example": false - }, - "active": { - "type": "boolean", - "example": true - }, - "copyleft": { - "type": "boolean", - "example": false - }, - "detector_type": { - "type": "integer", - "maximum": 2, - "minimum": 0, - "example": 1 - }, - "external_ref": { - "$ref": "#/definitions/datatypes.JSONType-models_LicenseDBSchemaExtension" - }, - "flag": { - "type": "integer", - "maximum": 2, - "minimum": 0, - "example": 1 - }, - "fullname": { - "type": "string", - "example": "MIT License" - }, - "marydone": { - "type": "boolean", - "example": false - }, - "notes": { - "type": "string", - "example": "This license has been superseded." - }, - "obligations": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Obligation" - } - }, - "risk": { - "type": "integer", - "maximum": 5, - "minimum": 0, - "example": 1 - }, - "source": { - "type": "string", - "example": "Source" - }, - "spdx_id": { - "type": "string", - "example": "MIT" - }, - "text": { - "type": "string", - "example": "MIT License Text here" - }, - "text_updatable": { - "type": "boolean", - "example": false - }, - "url": { - "type": "string", - "example": "https://opensource.org/licenses/MIT" - } - } - }, "models.Obligation": { "type": "object", "properties": { diff --git a/cmd/laas/docs/swagger.json b/cmd/laas/docs/swagger.json index dcb86c1..f681d19 100644 --- a/cmd/laas/docs/swagger.json +++ b/cmd/laas/docs/swagger.json @@ -676,75 +676,6 @@ } } } - }, - "patch": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Update a license in the service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Licenses" - ], - "summary": "Update a license", - "operationId": "UpdateLicense", - "parameters": [ - { - "type": "string", - "description": "Shortname of the license to be updated", - "name": "shortname", - "in": "path", - "required": true - }, - { - "description": "Update license body (requires only the fields to be updated)", - "name": "license", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/models.LicenseUpdateJSONSchema" - } - } - ], - "responses": { - "200": { - "description": "License updated successfully", - "schema": { - "$ref": "#/definitions/models.LicenseResponse" - } - }, - "400": { - "description": "Invalid license body", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "404": { - "description": "License with shortname not found", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "409": { - "description": "License with same shortname already exists", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - }, - "500": { - "description": "Failed to update license", - "schema": { - "$ref": "#/definitions/models.LicenseError" - } - } - } } }, "/login": { @@ -2493,10 +2424,6 @@ "models.LicenseId": { "type": "object", "properties": { - "id": { - "type": "integer", - "example": 31 - }, "shortname": { "type": "string", "example": "MIT" @@ -2591,98 +2518,6 @@ } } }, - "models.LicenseUpdateJSONSchema": { - "type": "object", - "properties": { - "FSFfree": { - "type": "boolean", - "example": false - }, - "Fedora": { - "type": "string", - "example": "Fedora" - }, - "GPLv2compatible": { - "type": "boolean", - "example": false - }, - "GPLv3compatible": { - "type": "boolean", - "example": false - }, - "OSIapproved": { - "type": "boolean", - "example": false - }, - "active": { - "type": "boolean", - "example": true - }, - "copyleft": { - "type": "boolean", - "example": false - }, - "detector_type": { - "type": "integer", - "maximum": 2, - "minimum": 0, - "example": 1 - }, - "external_ref": { - "$ref": "#/definitions/datatypes.JSONType-models_LicenseDBSchemaExtension" - }, - "flag": { - "type": "integer", - "maximum": 2, - "minimum": 0, - "example": 1 - }, - "fullname": { - "type": "string", - "example": "MIT License" - }, - "marydone": { - "type": "boolean", - "example": false - }, - "notes": { - "type": "string", - "example": "This license has been superseded." - }, - "obligations": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Obligation" - } - }, - "risk": { - "type": "integer", - "maximum": 5, - "minimum": 0, - "example": 1 - }, - "source": { - "type": "string", - "example": "Source" - }, - "spdx_id": { - "type": "string", - "example": "MIT" - }, - "text": { - "type": "string", - "example": "MIT License Text here" - }, - "text_updatable": { - "type": "boolean", - "example": false - }, - "url": { - "type": "string", - "example": "https://opensource.org/licenses/MIT" - } - } - }, "models.Obligation": { "type": "object", "properties": { diff --git a/cmd/laas/docs/swagger.yaml b/cmd/laas/docs/swagger.yaml index 8035b08..3d9dbda 100644 --- a/cmd/laas/docs/swagger.yaml +++ b/cmd/laas/docs/swagger.yaml @@ -197,9 +197,6 @@ definitions: type: object models.LicenseId: properties: - id: - example: 31 - type: integer shortname: example: MIT type: string @@ -263,75 +260,6 @@ definitions: type: string type: array type: object - models.LicenseUpdateJSONSchema: - properties: - FSFfree: - example: false - type: boolean - Fedora: - example: Fedora - type: string - GPLv2compatible: - example: false - type: boolean - GPLv3compatible: - example: false - type: boolean - OSIapproved: - example: false - type: boolean - active: - example: true - type: boolean - copyleft: - example: false - type: boolean - detector_type: - example: 1 - maximum: 2 - minimum: 0 - type: integer - external_ref: - $ref: '#/definitions/datatypes.JSONType-models_LicenseDBSchemaExtension' - flag: - example: 1 - maximum: 2 - minimum: 0 - type: integer - fullname: - example: MIT License - type: string - marydone: - example: false - type: boolean - notes: - example: This license has been superseded. - type: string - obligations: - items: - $ref: '#/definitions/models.Obligation' - type: array - risk: - example: 1 - maximum: 5 - minimum: 0 - type: integer - source: - example: Source - type: string - spdx_id: - example: MIT - type: string - text: - example: MIT License Text here - type: string - text_updatable: - example: false - type: boolean - url: - example: https://opensource.org/licenses/MIT - type: string - type: object models.Obligation: properties: active: @@ -1038,51 +966,6 @@ paths: summary: Get a license by shortname tags: - Licenses - patch: - consumes: - - application/json - description: Update a license in the service - operationId: UpdateLicense - parameters: - - description: Shortname of the license to be updated - in: path - name: shortname - required: true - type: string - - description: Update license body (requires only the fields to be updated) - in: body - name: license - required: true - schema: - $ref: '#/definitions/models.LicenseUpdateJSONSchema' - produces: - - application/json - responses: - "200": - description: License updated successfully - schema: - $ref: '#/definitions/models.LicenseResponse' - "400": - description: Invalid license body - schema: - $ref: '#/definitions/models.LicenseError' - "404": - description: License with shortname not found - schema: - $ref: '#/definitions/models.LicenseError' - "409": - description: License with same shortname already exists - schema: - $ref: '#/definitions/models.LicenseError' - "500": - description: Failed to update license - schema: - $ref: '#/definitions/models.LicenseError' - security: - - ApiKeyAuth: [] - summary: Update a license - tags: - - Licenses /licenses/export: get: description: Export all licenses as a json file diff --git a/cmd/laas/main.go b/cmd/laas/main.go index d2e7059..279398d 100644 --- a/cmd/laas/main.go +++ b/cmd/laas/main.go @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Kavya Shukla // SPDX-FileCopyrightText: 2023 Siemens AG // SPDX-FileContributor: Gaurav Mishra +// SPDX-FileContributor: Dearsh Oberoi // // SPDX-License-Identifier: GPL-2.0-only diff --git a/pkg/api/api.go b/pkg/api/api.go index 1ea1190..3d8d16d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -114,7 +114,7 @@ func Router() *gin.Engine { licenses.GET("/preview", GetAllLicensePreviews) licenses.POST("", CreateLicense) licenses.PATCH(":shortname", UpdateLicense) - licenses.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), ImportLicenses) + licenses.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), ImportLicenses) } search := authorizedv1.Group("/search") { @@ -122,13 +122,13 @@ func Router() *gin.Engine { } users := authorizedv1.Group("/users") { - users.GET("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.GetAllUser) + users.GET("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.GetAllUser) users.GET("/profile", auth.GetUserProfile) - users.GET(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.GetUser) - users.POST("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.CreateUser) + users.GET(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.GetUser) + users.POST("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.CreateUser) users.PATCH("", auth.UpdateProfile) - users.PATCH(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.UpdateUser) - users.DELETE(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.DeleteUser) + users.PATCH(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.UpdateUser) + users.DELETE(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.DeleteUser) } obligations := authorizedv1.Group("/obligations") { @@ -138,7 +138,7 @@ func Router() *gin.Engine { obligations.GET(":topic/audits", GetObligationAudits) obligations.GET("export", ExportObligations) obligations.POST("", CreateObligation) - obligations.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), ImportObligations) + obligations.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), ImportObligations) obligations.PATCH(":topic", UpdateObligation) obligations.DELETE(":topic", DeleteObligation) obligations.GET("/types", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), GetAllObligationType) @@ -226,18 +226,18 @@ func Router() *gin.Engine { } users := authorizedv1.Group("/users") { - users.GET("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.GetAllUser) + users.GET("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.GetAllUser) users.GET("/profile", auth.GetUserProfile) - users.GET(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.GetUser) - users.POST("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.CreateUser) - users.PATCH(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.UpdateUser) + users.GET(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.GetUser) + users.POST("", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.CreateUser) + users.PATCH(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.UpdateUser) users.PATCH("", auth.UpdateProfile) - users.DELETE(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), auth.DeleteUser) + users.DELETE(":username", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), auth.DeleteUser) } obligations := authorizedv1.Group("/obligations") { obligations.POST("", CreateObligation) - obligations.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), ImportObligations) + obligations.POST("import", middleware.RoleBasedAccessMiddleware([]string{"ADMIN", "SUPER_ADMIN"}), ImportObligations) obligations.PATCH(":topic", UpdateObligation) obligations.DELETE(":topic", DeleteObligation) obligations.GET("/types", middleware.RoleBasedAccessMiddleware([]string{"ADMIN"}), GetAllObligationType) diff --git a/pkg/api/licenses.go b/pkg/api/licenses.go index 7dcbcb3..f10991b 100644 --- a/pkg/api/licenses.go +++ b/pkg/api/licenses.go @@ -1,19 +1,20 @@ // SPDX-FileCopyrightText: 2023 Kavya Shukla // SPDX-FileCopyrightText: 2023 Siemens AG // SPDX-FileContributor: Gaurav Mishra +// SPDX-FileContributor: Dearsh Oberoi // // SPDX-License-Identifier: GPL-2.0-only package api import ( + "context" "encoding/json" "errors" "fmt" "io" "net/http" "path/filepath" - "reflect" "strconv" "strings" "time" @@ -227,8 +228,7 @@ func GetLicense(c *gin.Context) { return } - err := db.DB.Where(models.LicenseDB{Shortname: &queryParam}).First(&license).Error - + err := db.DB.Where(models.LicenseDB{Shortname: &queryParam}).Preload("User").First(&license).Error if err != nil { er := models.LicenseError{ Status: http.StatusNotFound, @@ -295,9 +295,13 @@ func CreateLicense(c *gin.Context) { return } - result := db.DB. + username := c.GetString("username") + ctx := context.WithValue(context.Background(), models.ContextKey("user"), username) + + result := db.DB.WithContext(ctx). Where(&models.LicenseDB{Shortname: input.Shortname}). FirstOrCreate(&input) + if result.Error != nil { er := models.LicenseError{ Status: http.StatusInternalServerError, @@ -320,6 +324,19 @@ func CreateLicense(c *gin.Context) { c.JSON(http.StatusConflict, er) return } + + if err := db.DB.Preload("User").First(&input).Error; err != nil { + er := models.LicenseError{ + Status: http.StatusInternalServerError, + Message: "Failed to create license", + Error: result.Error.Error(), + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + } + c.JSON(http.StatusInternalServerError, er) + return + } + res := models.LicenseResponse{ Data: []models.LicenseDB{input}, Status: http.StatusCreated, @@ -348,6 +365,8 @@ func CreateLicense(c *gin.Context) { // @Failure 500 {object} models.LicenseError "Failed to update license" // @Security ApiKeyAuth // @Router /licenses/{shortname} [patch] +type ContextKey string + func UpdateLicense(c *gin.Context) { _ = db.DB.Transaction(func(tx *gorm.DB) error { var updates models.LicenseUpdateJSONSchema @@ -454,7 +473,7 @@ func UpdateLicense(c *gin.Context) { return err } - if err := addChangelogsForLicenseUpdate(tx, username, &newLicense, &oldLicense); err != nil { + if err := utils.AddChangelogsForLicenseUpdate(tx, username, &newLicense, &oldLicense); err != nil { er := models.LicenseError{ Status: http.StatusInternalServerError, Message: "Failed to update license", @@ -480,264 +499,6 @@ func UpdateLicense(c *gin.Context) { }) } -// addChangelogsForLicenseUpdate adds changelogs for the updated fields on license update -func addChangelogsForLicenseUpdate(tx *gorm.DB, username string, - newLicense, oldLicense *models.LicenseDB) error { - var changes []models.ChangeLog - - if *oldLicense.Fullname != *newLicense.Fullname { - changes = append(changes, models.ChangeLog{ - Field: "Fullname", - OldValue: oldLicense.Fullname, - UpdatedValue: newLicense.Fullname, - }) - } - if *oldLicense.Url != *newLicense.Url { - changes = append(changes, models.ChangeLog{ - Field: "Url", - OldValue: oldLicense.Url, - UpdatedValue: newLicense.Url, - }) - } - if oldLicense.AddDate != newLicense.AddDate { - oldVal := oldLicense.AddDate.Format(time.RFC3339) - newVal := newLicense.AddDate.Format(time.RFC3339) - changes = append(changes, models.ChangeLog{ - Field: "Adddate", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Active != *newLicense.Active { - oldVal := strconv.FormatBool(*oldLicense.Active) - newVal := strconv.FormatBool(*newLicense.Active) - changes = append(changes, models.ChangeLog{ - Field: "Active", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Copyleft != *newLicense.Copyleft { - oldVal := strconv.FormatBool(*oldLicense.Copyleft) - newVal := strconv.FormatBool(*newLicense.Copyleft) - changes = append(changes, models.ChangeLog{ - Field: "Copyleft", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.FSFfree != *newLicense.FSFfree { - oldVal := strconv.FormatBool(*oldLicense.FSFfree) - newVal := strconv.FormatBool(*newLicense.FSFfree) - changes = append(changes, models.ChangeLog{ - Field: "FSFfree", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.GPLv2compatible != *newLicense.GPLv2compatible { - oldVal := strconv.FormatBool(*oldLicense.GPLv2compatible) - newVal := strconv.FormatBool(*newLicense.GPLv2compatible) - changes = append(changes, models.ChangeLog{ - Field: "GPLv2compatible", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.GPLv3compatible != *newLicense.GPLv3compatible { - oldVal := strconv.FormatBool(*oldLicense.GPLv3compatible) - newVal := strconv.FormatBool(*newLicense.GPLv3compatible) - changes = append(changes, models.ChangeLog{ - Field: "GPLv3compatible", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.OSIapproved != *newLicense.OSIapproved { - oldVal := strconv.FormatBool(*oldLicense.OSIapproved) - newVal := strconv.FormatBool(*newLicense.OSIapproved) - changes = append(changes, models.ChangeLog{ - Field: "OSIapproved", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Text != *newLicense.Text { - changes = append(changes, models.ChangeLog{ - Field: "Text", - OldValue: oldLicense.Text, - UpdatedValue: newLicense.Text, - }) - } - if *oldLicense.TextUpdatable != *newLicense.TextUpdatable { - oldVal := strconv.FormatBool(*oldLicense.TextUpdatable) - newVal := strconv.FormatBool(*newLicense.TextUpdatable) - changes = append(changes, models.ChangeLog{ - Field: "TextUpdatable", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Fedora != *newLicense.Fedora { - changes = append(changes, models.ChangeLog{ - Field: "Fedora", - OldValue: oldLicense.Fedora, - UpdatedValue: newLicense.Fedora, - }) - } - if *oldLicense.Flag != *newLicense.Flag { - oldVal := strconv.FormatInt(*oldLicense.Flag, 10) - newVal := strconv.FormatInt(*newLicense.Flag, 10) - changes = append(changes, models.ChangeLog{ - Field: "Flag", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Notes != *newLicense.Notes { - changes = append(changes, models.ChangeLog{ - Field: "Notes", - OldValue: oldLicense.Notes, - UpdatedValue: newLicense.Notes, - }) - } - if *oldLicense.DetectorType != *newLicense.DetectorType { - oldVal := strconv.FormatInt(*oldLicense.DetectorType, 10) - newVal := strconv.FormatInt(*newLicense.DetectorType, 10) - changes = append(changes, models.ChangeLog{ - Field: "DetectorType", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Source != *newLicense.Source { - changes = append(changes, models.ChangeLog{ - Field: "Source", - OldValue: oldLicense.Source, - UpdatedValue: newLicense.Source, - }) - } - if *oldLicense.SpdxId != *newLicense.SpdxId { - changes = append(changes, models.ChangeLog{ - Field: "SpdxId", - OldValue: oldLicense.SpdxId, - UpdatedValue: newLicense.SpdxId, - }) - } - if *oldLicense.Risk != *newLicense.Risk { - oldVal := strconv.FormatInt(*oldLicense.Risk, 10) - newVal := strconv.FormatInt(*newLicense.Risk, 10) - changes = append(changes, models.ChangeLog{ - Field: "Risk", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - if *oldLicense.Marydone != *newLicense.Marydone { - oldVal := strconv.FormatBool(*oldLicense.Marydone) - newVal := strconv.FormatBool(*newLicense.Marydone) - changes = append(changes, models.ChangeLog{ - Field: "Marydone", - OldValue: &oldVal, - UpdatedValue: &newVal, - }) - } - - oldLicenseExternalRef := oldLicense.ExternalRef.Data() - oldExternalRefVal := reflect.ValueOf(oldLicenseExternalRef) - typesOf := oldExternalRefVal.Type() - - newLicenseExternalRef := newLicense.ExternalRef.Data() - newExternalRefVal := reflect.ValueOf(newLicenseExternalRef) - - for i := 0; i < oldExternalRefVal.NumField(); i++ { - fieldName := typesOf.Field(i).Name - - switch typesOf.Field(i).Type.String() { - case "*boolean": - oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*bool) - newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*bool) - if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || - ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { - var oldVal, newVal *string - oldVal, newVal = nil, nil - - if oldFieldPtr != nil { - _oldVal := fmt.Sprintf("%t", *oldFieldPtr) - oldVal = &_oldVal - } - - if newFieldPtr != nil { - _newVal := fmt.Sprintf("%t", *newFieldPtr) - newVal = &_newVal - } - - changes = append(changes, models.ChangeLog{ - Field: fmt.Sprintf("ExternalRef.%s", fieldName), - OldValue: oldVal, - UpdatedValue: newVal, - }) - } - case "*string": - oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*string) - newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*string) - if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || - ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { - changes = append(changes, models.ChangeLog{ - Field: fmt.Sprintf("ExternalRef.%s", fieldName), - OldValue: oldFieldPtr, - UpdatedValue: newFieldPtr, - }) - } - case "*int": - oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*int) - newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*int) - if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || - ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { - var oldVal, newVal *string - oldVal, newVal = nil, nil - - if oldFieldPtr != nil { - _oldVal := fmt.Sprintf("%d", *oldFieldPtr) - oldVal = &_oldVal - } - - if newFieldPtr != nil { - _newVal := fmt.Sprintf("%d", *newFieldPtr) - newVal = &_newVal - } - - changes = append(changes, models.ChangeLog{ - Field: fmt.Sprintf("ExternalRef.%s", fieldName), - OldValue: oldVal, - UpdatedValue: newVal, - }) - } - } - } - - if len(changes) != 0 { - var user models.User - if err := tx.Where(models.User{Username: &username}).First(&user).Error; err != nil { - return err - } - - audit := models.Audit{ - UserId: user.Id, - TypeId: newLicense.Id, - Timestamp: time.Now(), - Type: "license", - ChangeLogs: changes, - } - - if err := tx.Create(&audit).Error; err != nil { - return err - } - } - - return nil -} - // SearchInLicense Search for license data based on user-provided search criteria. // // @Summary Search licenses @@ -909,66 +670,40 @@ func ImportLicenses(c *gin.Context) { } for i := range licenses { - _ = db.DB.Transaction(func(tx *gorm.DB) error { - errMessage, importStatus, oldLicense, newLicense := utils.InsertOrUpdateLicenseOnImport(tx, &licenses[i], &externalRefs[i]) - - if importStatus == utils.IMPORT_FAILED { - erroredLicense := "" - if licenses[i].Shortname != nil { - erroredLicense = *licenses[i].Shortname - } - res.Data = append(res.Data, models.LicenseError{ - Status: http.StatusInternalServerError, - Message: errMessage, - Error: erroredLicense, - Path: c.Request.URL.Path, - Timestamp: time.Now().Format(time.RFC3339), - }) - return errors.New(errMessage) - } else if importStatus == utils.IMPORT_LICENSE_CREATED { - res.Data = append(res.Data, models.LicenseImportStatus{ - Data: models.LicenseId{Id: oldLicense.Id, Shortname: *oldLicense.Shortname}, - Status: http.StatusCreated, - }) - } else if importStatus == utils.IMPORT_LICENSE_UPDATED { - if err := addChangelogsForLicenseUpdate(tx, username, newLicense, oldLicense); err != nil { - res.Data = append(res.Data, models.LicenseError{ - Status: http.StatusInternalServerError, - Message: "Failed to update license", - Error: *newLicense.Shortname, - Path: c.Request.URL.Path, - Timestamp: time.Now().Format(time.RFC3339), - }) - return err - } - res.Data = append(res.Data, models.LicenseImportStatus{ - Data: models.LicenseId{Id: newLicense.Id, Shortname: *newLicense.Shortname}, - Status: http.StatusOK, - }) - } else if importStatus == utils.IMPORT_LICENSE_UPDATED_EXCEPT_TEXT { - if err := addChangelogsForLicenseUpdate(tx, username, newLicense, oldLicense); err != nil { - res.Data = append(res.Data, models.LicenseError{ - Status: http.StatusInternalServerError, - Message: "Failed to update license", - Error: *newLicense.Shortname, - Path: c.Request.URL.Path, - Timestamp: time.Now().Format(time.RFC3339), - }) - return err - } + errMessage, importStatus := utils.InsertOrUpdateLicenseOnImport(&licenses[i], &externalRefs[i], username) - res.Data = append(res.Data, models.LicenseError{ - Status: http.StatusConflict, - Message: errMessage, - Error: *newLicense.Shortname, - Path: c.Request.URL.Path, - Timestamp: time.Now().Format(time.RFC3339), - }) - // error is not returned here as it will rollback the transaction + if importStatus == utils.IMPORT_FAILED { + erroredLicense := "" + if licenses[i].Shortname != nil { + erroredLicense = *licenses[i].Shortname } - - return nil - }) + res.Data = append(res.Data, models.LicenseError{ + Status: http.StatusInternalServerError, + Message: errMessage, + Error: erroredLicense, + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + }) + } else if importStatus == utils.IMPORT_LICENSE_CREATED { + res.Data = append(res.Data, models.LicenseImportStatus{ + Data: models.LicenseId{Shortname: *licenses[i].Shortname}, + Status: http.StatusCreated, + }) + } else if importStatus == utils.IMPORT_LICENSE_UPDATED { + res.Data = append(res.Data, models.LicenseImportStatus{ + Data: models.LicenseId{Shortname: *licenses[i].Shortname}, + Status: http.StatusOK, + }) + } else if importStatus == utils.IMPORT_LICENSE_UPDATED_EXCEPT_TEXT { + res.Data = append(res.Data, models.LicenseError{ + Status: http.StatusConflict, + Message: errMessage, + Error: *licenses[i].Shortname, + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + }) + // error is not returned here as it will rollback the transaction + } } c.JSON(http.StatusOK, res) diff --git a/pkg/models/types.go b/pkg/models/types.go index 1f96b78..38d389f 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -51,7 +51,35 @@ type LicenseDB struct { User User `gorm:"foreignKey:UserId;references:Id" json:"user"` // Reference to User } -func (l *LicenseDB) BeforeSave(tx *gorm.DB) (err error) { +// BeforeCreate hook to validate data and log the user who is creating the record +func (l *LicenseDB) BeforeCreate(tx *gorm.DB) (err error) { + username, ok := tx.Statement.Context.Value(ContextKey("user")).(string) + if !ok { + return errors.New("username not found in context") + } + + var user User + if err := tx.Where("username = ?", username).First(&user).Error; err != nil { + return errors.New("user not found") + } + l.UserId = user.Id + + if err := validateLicenseFields(l); err != nil { + return err + } + return nil +} + +// BeforeUpdate hook to validate data and log the user who is updating the record +func (l *LicenseDB) BeforeUpdate(tx *gorm.DB) (err error) { + if err := validateLicenseFields(l); err != nil { + return err + } + return nil +} + +// Helper function to validate fields +func validateLicenseFields(l *LicenseDB) error { if l.Shortname != nil && *l.Shortname == "" { return errors.New("shortname cannot be an empty string") } @@ -64,7 +92,7 @@ func (l *LicenseDB) BeforeSave(tx *gorm.DB) (err error) { if l.SpdxId != nil && *l.SpdxId == "" { return errors.New("spdx_id cannot be an empty string") } - if l.Risk != nil && (*l.Risk < 0 && *l.Risk > 5) { + if l.Risk != nil && (*l.Risk < 0 || *l.Risk > 5) { return errors.New("risk can have values from 0 to 5 only") } if l.Flag != nil && (*l.Flag < 0 || *l.Flag > 2) { @@ -73,7 +101,7 @@ func (l *LicenseDB) BeforeSave(tx *gorm.DB) (err error) { if l.DetectorType != nil && (*l.DetectorType < 0 || *l.DetectorType > 2) { return errors.New("detector_type can have values from 0 to 2 only") } - return + return nil } // LicenseUpdateJSONSchema struct represents the input format for updating an existing license. @@ -141,7 +169,6 @@ type LicensePreviewResponse struct { // LicenseId is the id of successfully imported license type LicenseId struct { - Id int64 `json:"id" example:"31"` Shortname string `json:"shortname" example:"MIT"` } @@ -460,7 +487,6 @@ func (o *Obligation) BeforeCreate(tx *gorm.DB) (err error) { type ContextKey string func (o *Obligation) BeforeUpdate(tx *gorm.DB) (err error) { - oldObligation, ok := tx.Statement.Context.Value(ContextKey("oldObligation")).(*Obligation) if !ok { return errors.New("something went wrong") diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 7cbf2b3..f93fd67 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -8,6 +8,7 @@ package utils import ( + "context" "encoding/base64" "encoding/json" "errors" @@ -15,6 +16,7 @@ import ( "log" "net/http" "os" + "reflect" "strconv" "strings" "time" @@ -201,7 +203,7 @@ const ( IMPORT_LICENSE_UPDATED_EXCEPT_TEXT ) -func InsertOrUpdateLicenseOnImport(tx *gorm.DB, license *models.LicenseDB, externalRefs *models.UpdateExternalRefsJSONPayload) (string, LicenseImportStatusCode, *models.LicenseDB, *models.LicenseDB) { +func InsertOrUpdateLicenseOnImport(license *models.LicenseDB, externalRefs *models.UpdateExternalRefsJSONPayload, username string) (string, LicenseImportStatusCode) { var message string var importStatus LicenseImportStatusCode var newLicense, oldLicense models.LicenseDB @@ -211,58 +213,78 @@ func InsertOrUpdateLicenseOnImport(tx *gorm.DB, license *models.LicenseDB, exter if err := validate.Struct(license); err != nil { message = fmt.Sprintf("field '%s' failed validation: %s\n", err.(validator.ValidationErrors)[0].Field(), err.(validator.ValidationErrors)[0].Tag()) importStatus = IMPORT_FAILED - return message, importStatus, &oldLicense, &newLicense + return message, importStatus } - result := tx. - Where(&models.LicenseDB{Shortname: license.Shortname}). - Attrs(license). - FirstOrCreate(&oldLicense) - if result.Error != nil { - message = fmt.Sprintf("failed to create license: %s", result.Error.Error()) - importStatus = IMPORT_FAILED - return message, importStatus, &oldLicense, &newLicense - } else if result.RowsAffected == 0 { - // case when license exists in database and is updated - - // Overwrite values of existing keys, add new key value pairs and remove keys with null values. - if err := tx.Model(&models.LicenseDB{}).Where(models.LicenseDB{Id: oldLicense.Id}).UpdateColumn("external_ref", gorm.Expr("jsonb_strip_nulls(COALESCE(external_ref, '{}'::jsonb) || ?)", &externalRefs.ExternalRef)).Error; err != nil { - message = fmt.Sprintf("failed to update license: %s", err.Error()) + _ = db.DB.Transaction(func(tx *gorm.DB) error { + ctx := context.WithValue(context.Background(), models.ContextKey("user"), username) + result := tx.WithContext(ctx). + Where(&models.LicenseDB{Shortname: license.Shortname}). + Attrs(license). + FirstOrCreate(&oldLicense) + if result.Error != nil { + message = fmt.Sprintf("failed to import license: %s", result.Error.Error()) importStatus = IMPORT_FAILED - return message, importStatus, &oldLicense, &newLicense - } + return errors.New(message) + } else if result.RowsAffected == 0 { + // case when license exists in database and is updated + + // Overwrite values of existing keys, add new key value pairs and remove keys with null values. + if err := tx.Model(&models.LicenseDB{}).Where(models.LicenseDB{Id: oldLicense.Id}).UpdateColumn("external_ref", gorm.Expr("jsonb_strip_nulls(COALESCE(external_ref, '{}'::jsonb) || ?)", &externalRefs.ExternalRef)).Error; err != nil { + message = fmt.Sprintf("failed to update license: %s", err.Error()) + importStatus = IMPORT_FAILED + return errors.New(message) + } - // https://github.com/go-gorm/gorm/issues/3938: BeforeSave hook is called on the struct passed in .Model() - // Cannot pass empty newLicense struct in .Model() as all fields will be empty and no validation will happen - newLicense = *license + // https://github.com/go-gorm/gorm/issues/3938: BeforeSave hook is called on the struct passed in .Model() + // Cannot pass empty newLicense struct in .Model() as all fields will be empty and no validation will happen + newLicense = *license - // Update all other fields except external_ref and rf_shortname - query := tx.Model(&newLicense).Where(&models.LicenseDB{Id: oldLicense.Id}).Omit("external_ref", "rf_shortname") + // Update all other fields except external_ref and rf_shortname + query := tx.Model(&newLicense).Where(&models.LicenseDB{Id: oldLicense.Id}).Omit("external_ref", "rf_shortname") - // Do not update text in import if it was modified manually - if *oldLicense.Flag == 2 { - query = query.Omit("rf_text") - } + // Do not update text in import if it was modified manually + if *oldLicense.Flag == 2 { + query = query.Omit("rf_text") + } - if err := query.Clauses(clause.Returning{}).Updates(&newLicense).Error; err != nil { - message = fmt.Sprintf("failed to update license: %s", err.Error()) - importStatus = IMPORT_FAILED - return message, importStatus, &oldLicense, &newLicense - } + if err := query.Clauses(clause.Returning{}).Updates(&newLicense).Error; err != nil { + message = fmt.Sprintf("failed to update license: %s", err.Error()) + importStatus = IMPORT_FAILED + return errors.New(message) + } + + if *newLicense.Flag == 2 { + message = "all fields except text were updated. text was updated manually and cannot be overwritten in an import." + importStatus = IMPORT_LICENSE_UPDATED_EXCEPT_TEXT + // error is not returned here as it will rollback the transaction + } else { + importStatus = IMPORT_LICENSE_UPDATED + } - if *newLicense.Flag == 2 { - message = "all fields except text were updated. text was updated manually and cannot be overwritten in an import." - importStatus = IMPORT_LICENSE_UPDATED_EXCEPT_TEXT - // error is not returned here as it will rollback the transaction + if err := AddChangelogsForLicenseUpdate(tx, username, &newLicense, &oldLicense); err != nil { + message = fmt.Sprintf("failed to update license: %s", err.Error()) + importStatus = IMPORT_FAILED + return errors.New(message) + } } else { - importStatus = IMPORT_LICENSE_UPDATED + // case when license doesn't exist in database and is inserted + importStatus = IMPORT_LICENSE_CREATED } + + return nil + }) + + if importStatus == IMPORT_FAILED { + red := "\033[31m" + reset := "\033[0m" + log.Printf("%s%s: %s%s", red, *license.Shortname, message, reset) } else { - // case when license doesn't exist in database and is inserted - importStatus = IMPORT_LICENSE_CREATED + green := "\033[32m" + reset := "\033[0m" + log.Printf("%sImport for license '%s' successful%s", green, *license.Shortname, reset) } - - return message, importStatus, &oldLicense, &newLicense + return message, importStatus } // GenerateDiffForReplacingLicenses creates list of license associations to be created and deleted such that the list of currently associated @@ -399,20 +421,16 @@ func Populatedb(datafile string) { if err := json.Unmarshal(byteResult, &licenses); err != nil { log.Fatalf("error reading from json file: %v", err) } + + user := models.User{} + level := "SUPER_ADMIN" + if err := db.DB.Where(&models.User{Userlevel: &level}).First(&user).Error; err != nil { + log.Fatalf("Failed to find a super admin") + } + for _, license := range licenses { result := Converter(license) - _ = db.DB.Transaction(func(tx *gorm.DB) error { - errMessage, importStatus, _, _ := InsertOrUpdateLicenseOnImport(tx, &result, &models.UpdateExternalRefsJSONPayload{ExternalRef: make(map[string]interface{})}) - if importStatus == IMPORT_FAILED { - // ANSI escape code for red text - red := "\033[31m" - reset := "\033[0m" - log.Printf("%s%s: %s%s", red, *result.Shortname, errMessage, reset) - return errors.New(errMessage) - } - return nil - }) - + _, _ = InsertOrUpdateLicenseOnImport(&result, &models.UpdateExternalRefsJSONPayload{ExternalRef: make(map[string]interface{})}, *user.Username) } } @@ -492,3 +510,261 @@ func GetKid(token string) (string, error) { err = json.Unmarshal(decodedBytes, &header) return header.KeyID, err } + +// addChangelogsForLicenseUpdate adds changelogs for the updated fields on license update +func AddChangelogsForLicenseUpdate(tx *gorm.DB, username string, + newLicense, oldLicense *models.LicenseDB) error { + var changes []models.ChangeLog + + if *oldLicense.Fullname != *newLicense.Fullname { + changes = append(changes, models.ChangeLog{ + Field: "Fullname", + OldValue: oldLicense.Fullname, + UpdatedValue: newLicense.Fullname, + }) + } + if *oldLicense.Url != *newLicense.Url { + changes = append(changes, models.ChangeLog{ + Field: "Url", + OldValue: oldLicense.Url, + UpdatedValue: newLicense.Url, + }) + } + if oldLicense.AddDate != newLicense.AddDate { + oldVal := oldLicense.AddDate.Format(time.RFC3339) + newVal := newLicense.AddDate.Format(time.RFC3339) + changes = append(changes, models.ChangeLog{ + Field: "Adddate", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Active != *newLicense.Active { + oldVal := strconv.FormatBool(*oldLicense.Active) + newVal := strconv.FormatBool(*newLicense.Active) + changes = append(changes, models.ChangeLog{ + Field: "Active", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Copyleft != *newLicense.Copyleft { + oldVal := strconv.FormatBool(*oldLicense.Copyleft) + newVal := strconv.FormatBool(*newLicense.Copyleft) + changes = append(changes, models.ChangeLog{ + Field: "Copyleft", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.FSFfree != *newLicense.FSFfree { + oldVal := strconv.FormatBool(*oldLicense.FSFfree) + newVal := strconv.FormatBool(*newLicense.FSFfree) + changes = append(changes, models.ChangeLog{ + Field: "FSFfree", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.GPLv2compatible != *newLicense.GPLv2compatible { + oldVal := strconv.FormatBool(*oldLicense.GPLv2compatible) + newVal := strconv.FormatBool(*newLicense.GPLv2compatible) + changes = append(changes, models.ChangeLog{ + Field: "GPLv2compatible", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.GPLv3compatible != *newLicense.GPLv3compatible { + oldVal := strconv.FormatBool(*oldLicense.GPLv3compatible) + newVal := strconv.FormatBool(*newLicense.GPLv3compatible) + changes = append(changes, models.ChangeLog{ + Field: "GPLv3compatible", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.OSIapproved != *newLicense.OSIapproved { + oldVal := strconv.FormatBool(*oldLicense.OSIapproved) + newVal := strconv.FormatBool(*newLicense.OSIapproved) + changes = append(changes, models.ChangeLog{ + Field: "OSIapproved", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Text != *newLicense.Text { + changes = append(changes, models.ChangeLog{ + Field: "Text", + OldValue: oldLicense.Text, + UpdatedValue: newLicense.Text, + }) + } + if *oldLicense.TextUpdatable != *newLicense.TextUpdatable { + oldVal := strconv.FormatBool(*oldLicense.TextUpdatable) + newVal := strconv.FormatBool(*newLicense.TextUpdatable) + changes = append(changes, models.ChangeLog{ + Field: "TextUpdatable", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Fedora != *newLicense.Fedora { + changes = append(changes, models.ChangeLog{ + Field: "Fedora", + OldValue: oldLicense.Fedora, + UpdatedValue: newLicense.Fedora, + }) + } + if *oldLicense.Flag != *newLicense.Flag { + oldVal := strconv.FormatInt(*oldLicense.Flag, 10) + newVal := strconv.FormatInt(*newLicense.Flag, 10) + changes = append(changes, models.ChangeLog{ + Field: "Flag", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Notes != *newLicense.Notes { + changes = append(changes, models.ChangeLog{ + Field: "Notes", + OldValue: oldLicense.Notes, + UpdatedValue: newLicense.Notes, + }) + } + if *oldLicense.DetectorType != *newLicense.DetectorType { + oldVal := strconv.FormatInt(*oldLicense.DetectorType, 10) + newVal := strconv.FormatInt(*newLicense.DetectorType, 10) + changes = append(changes, models.ChangeLog{ + Field: "DetectorType", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Source != *newLicense.Source { + changes = append(changes, models.ChangeLog{ + Field: "Source", + OldValue: oldLicense.Source, + UpdatedValue: newLicense.Source, + }) + } + if *oldLicense.SpdxId != *newLicense.SpdxId { + changes = append(changes, models.ChangeLog{ + Field: "SpdxId", + OldValue: oldLicense.SpdxId, + UpdatedValue: newLicense.SpdxId, + }) + } + if *oldLicense.Risk != *newLicense.Risk { + oldVal := strconv.FormatInt(*oldLicense.Risk, 10) + newVal := strconv.FormatInt(*newLicense.Risk, 10) + changes = append(changes, models.ChangeLog{ + Field: "Risk", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + if *oldLicense.Marydone != *newLicense.Marydone { + oldVal := strconv.FormatBool(*oldLicense.Marydone) + newVal := strconv.FormatBool(*newLicense.Marydone) + changes = append(changes, models.ChangeLog{ + Field: "Marydone", + OldValue: &oldVal, + UpdatedValue: &newVal, + }) + } + + oldLicenseExternalRef := oldLicense.ExternalRef.Data() + oldExternalRefVal := reflect.ValueOf(oldLicenseExternalRef) + typesOf := oldExternalRefVal.Type() + + newLicenseExternalRef := newLicense.ExternalRef.Data() + newExternalRefVal := reflect.ValueOf(newLicenseExternalRef) + + for i := 0; i < oldExternalRefVal.NumField(); i++ { + fieldName := typesOf.Field(i).Name + + switch typesOf.Field(i).Type.String() { + case "*boolean": + oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*bool) + newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*bool) + if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || + ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { + var oldVal, newVal *string + oldVal, newVal = nil, nil + + if oldFieldPtr != nil { + _oldVal := fmt.Sprintf("%t", *oldFieldPtr) + oldVal = &_oldVal + } + + if newFieldPtr != nil { + _newVal := fmt.Sprintf("%t", *newFieldPtr) + newVal = &_newVal + } + + changes = append(changes, models.ChangeLog{ + Field: fmt.Sprintf("ExternalRef.%s", fieldName), + OldValue: oldVal, + UpdatedValue: newVal, + }) + } + case "*string": + oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*string) + newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*string) + if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || + ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { + changes = append(changes, models.ChangeLog{ + Field: fmt.Sprintf("ExternalRef.%s", fieldName), + OldValue: oldFieldPtr, + UpdatedValue: newFieldPtr, + }) + } + case "*int": + oldFieldPtr, _ := oldExternalRefVal.Field(i).Interface().(*int) + newFieldPtr, _ := newExternalRefVal.Field(i).Interface().(*int) + if (oldFieldPtr == nil && newFieldPtr != nil) || (oldFieldPtr != nil && newFieldPtr == nil) || + ((oldFieldPtr != nil && newFieldPtr != nil) && (*oldFieldPtr != *newFieldPtr)) { + var oldVal, newVal *string + oldVal, newVal = nil, nil + + if oldFieldPtr != nil { + _oldVal := fmt.Sprintf("%d", *oldFieldPtr) + oldVal = &_oldVal + } + + if newFieldPtr != nil { + _newVal := fmt.Sprintf("%d", *newFieldPtr) + newVal = &_newVal + } + + changes = append(changes, models.ChangeLog{ + Field: fmt.Sprintf("ExternalRef.%s", fieldName), + OldValue: oldVal, + UpdatedValue: newVal, + }) + } + } + } + + if len(changes) != 0 { + var user models.User + if err := tx.Where(models.User{Username: &username}).First(&user).Error; err != nil { + return err + } + + audit := models.Audit{ + UserId: user.Id, + TypeId: newLicense.Id, + Timestamp: time.Now(), + Type: "license", + ChangeLogs: changes, + } + + if err := tx.Create(&audit).Error; err != nil { + return err + } + } + + return nil +} diff --git a/swagger-diff.txt b/swagger-diff.txt deleted file mode 100644 index e69de29..0000000