From f338879213ae62cc1cac0d6033bb323f93ae7151 Mon Sep 17 00:00:00 2001 From: Gaurav Mishra Date: Wed, 20 Dec 2023 12:02:47 +0530 Subject: [PATCH] feat(api): add endpoints to get obligation maps Signed-off-by: Gaurav Mishra --- cmd/laas/docs/docs.go | 115 ++++++++++++++++++++++++++++ cmd/laas/docs/swagger.json | 115 ++++++++++++++++++++++++++++ cmd/laas/docs/swagger.yaml | 77 +++++++++++++++++++ pkg/api/api.go | 5 ++ pkg/api/obligationmap.go | 153 +++++++++++++++++++++++++++++++++++++ pkg/models/types.go | 23 ++++-- 6 files changed, 483 insertions(+), 5 deletions(-) create mode 100644 pkg/api/obligationmap.go diff --git a/cmd/laas/docs/docs.go b/cmd/laas/docs/docs.go index 99804f6..ecd3512 100644 --- a/cmd/laas/docs/docs.go +++ b/cmd/laas/docs/docs.go @@ -465,6 +465,84 @@ const docTemplate = `{ } } }, + "/obligation_maps/license/{license}": { + "get": { + "description": "Get obligation maps for a given license shortname", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Obligations" + ], + "summary": "Get maps for a license", + "operationId": "GetObligationMapByLicense", + "parameters": [ + { + "type": "string", + "description": "Shortname of the license", + "name": "license", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ObligationMapResponse" + } + }, + "404": { + "description": "No license with given shortname found or no map for", + "schema": { + "$ref": "#/definitions/models.LicenseError" + } + } + } + } + }, + "/obligation_maps/topic/{topic}": { + "get": { + "description": "Get obligation maps for a given obligation topic", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Obligations" + ], + "summary": "Get maps for an obligation", + "operationId": "GetObligationMapByTopic", + "parameters": [ + { + "type": "string", + "description": "Topic of the obligation", + "name": "topic", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ObligationMapResponse" + } + }, + "404": { + "description": "No obligation with given topic found or no map for", + "schema": { + "$ref": "#/definitions/models.LicenseError" + } + } + } + } + }, "/obligations": { "get": { "description": "Get all active obligations from the service", @@ -1351,6 +1429,43 @@ const docTemplate = `{ } } }, + "models.ObligationMapResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ObligationMapUser" + } + }, + "paginationmeta": { + "$ref": "#/definitions/models.PaginationMeta" + }, + "status": { + "type": "integer", + "example": 200 + } + } + }, + "models.ObligationMapUser": { + "type": "object", + "properties": { + "shortnames": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "GPL-2.0-only", + "GPL-2.0-or-later" + ] + }, + "topic": { + "type": "string", + "example": "copyleft" + } + } + }, "models.ObligationResponse": { "type": "object", "properties": { diff --git a/cmd/laas/docs/swagger.json b/cmd/laas/docs/swagger.json index d580103..393a080 100644 --- a/cmd/laas/docs/swagger.json +++ b/cmd/laas/docs/swagger.json @@ -459,6 +459,84 @@ } } }, + "/obligation_maps/license/{license}": { + "get": { + "description": "Get obligation maps for a given license shortname", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Obligations" + ], + "summary": "Get maps for a license", + "operationId": "GetObligationMapByLicense", + "parameters": [ + { + "type": "string", + "description": "Shortname of the license", + "name": "license", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ObligationMapResponse" + } + }, + "404": { + "description": "No license with given shortname found or no map for", + "schema": { + "$ref": "#/definitions/models.LicenseError" + } + } + } + } + }, + "/obligation_maps/topic/{topic}": { + "get": { + "description": "Get obligation maps for a given obligation topic", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Obligations" + ], + "summary": "Get maps for an obligation", + "operationId": "GetObligationMapByTopic", + "parameters": [ + { + "type": "string", + "description": "Topic of the obligation", + "name": "topic", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ObligationMapResponse" + } + }, + "404": { + "description": "No obligation with given topic found or no map for", + "schema": { + "$ref": "#/definitions/models.LicenseError" + } + } + } + } + }, "/obligations": { "get": { "description": "Get all active obligations from the service", @@ -1345,6 +1423,43 @@ } } }, + "models.ObligationMapResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ObligationMapUser" + } + }, + "paginationmeta": { + "$ref": "#/definitions/models.PaginationMeta" + }, + "status": { + "type": "integer", + "example": 200 + } + } + }, + "models.ObligationMapUser": { + "type": "object", + "properties": { + "shortnames": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "GPL-2.0-only", + "GPL-2.0-or-later" + ] + }, + "topic": { + "type": "string", + "example": "copyleft" + } + } + }, "models.ObligationResponse": { "type": "object", "properties": { diff --git a/cmd/laas/docs/swagger.yaml b/cmd/laas/docs/swagger.yaml index 1ffd17b..16f0c12 100644 --- a/cmd/laas/docs/swagger.yaml +++ b/cmd/laas/docs/swagger.yaml @@ -330,6 +330,31 @@ definitions: - topic - type type: object + models.ObligationMapResponse: + properties: + data: + items: + $ref: '#/definitions/models.ObligationMapUser' + type: array + paginationmeta: + $ref: '#/definitions/models.PaginationMeta' + status: + example: 200 + type: integer + type: object + models.ObligationMapUser: + properties: + shortnames: + example: + - GPL-2.0-only + - GPL-2.0-or-later + items: + type: string + type: array + topic: + example: copyleft + type: string + type: object models.ObligationResponse: properties: data: @@ -754,6 +779,58 @@ paths: summary: Update a license tags: - Licenses + /obligation_maps/license/{license}: + get: + consumes: + - application/json + description: Get obligation maps for a given license shortname + operationId: GetObligationMapByLicense + parameters: + - description: Shortname of the license + in: path + name: license + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ObligationMapResponse' + "404": + description: No license with given shortname found or no map for + schema: + $ref: '#/definitions/models.LicenseError' + summary: Get maps for a license + tags: + - Obligations + /obligation_maps/topic/{topic}: + get: + consumes: + - application/json + description: Get obligation maps for a given obligation topic + operationId: GetObligationMapByTopic + parameters: + - description: Topic of the obligation + in: path + name: topic + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ObligationMapResponse' + "404": + description: No obligation with given topic found or no map for + schema: + $ref: '#/definitions/models.LicenseError' + summary: Get maps for an obligation + tags: + - Obligations /obligations: get: consumes: diff --git a/pkg/api/api.go b/pkg/api/api.go index 3557811..c390056 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -61,6 +61,11 @@ func Router() *gin.Engine { obligations.GET("", GetAllObligation) obligations.GET(":topic", GetObligation) } + obMap := unAuthorizedv1.Group("/obligation_maps") + { + obMap.GET("topic/:topic", GetObligationMapByTopic) + obMap.GET("license/:license", GetObligationMapByLicense) + } } authorizedv1 := r.Group("/api/v1") diff --git a/pkg/api/obligationmap.go b/pkg/api/obligationmap.go new file mode 100644 index 0000000..f0475c8 --- /dev/null +++ b/pkg/api/obligationmap.go @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2023 Siemens AG +// SPDX-FileContributor: Gaurav Mishra +// +// SPDX-License-Identifier: GPL-2.0-only + +package api + +import ( + "fmt" + "net/http" + "time" + + "github.com/fossology/LicenseDb/pkg/db" + "github.com/fossology/LicenseDb/pkg/models" + "github.com/gin-gonic/gin" +) + +// GetObligationMapByTopic retrieves obligation maps for a given obligation topic +// +// @Summary Get maps for an obligation +// @Description Get obligation maps for a given obligation topic +// @Id GetObligationMapByTopic +// @Tags Obligations +// @Accept json +// @Produce json +// @Param topic path string true "Topic of the obligation" +// @Success 200 {object} models.ObligationMapResponse +// @Failure 404 {object} models.LicenseError "No obligation with given topic found or no map for +// obligation exists" +// @Router /obligation_maps/topic/{topic} [get] +func GetObligationMapByTopic(c *gin.Context) { + var obligation models.Obligation + var obMap []models.ObligationMap + var resObMap models.ObligationMapUser + var shortnameList []string + + topic := c.Param("topic") + query := db.DB.Model(&obligation) + + if err := query.Where(models.Obligation{Topic: topic}).First(&obligation).Error; err != nil { + er := models.LicenseError{ + Status: http.StatusNotFound, + Message: fmt.Sprintf("obligation with topic '%s' not found", topic), + Error: err.Error(), + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + } + c.JSON(http.StatusNotFound, er) + return + } + + query = db.DB.Model(&obMap) + + if err := query.Where(models.ObligationMap{ObligationPk: obligation.Id}).Find(&obMap).Error; err != nil { + er := models.LicenseError{ + Status: http.StatusNotFound, + Message: fmt.Sprintf("Obligation map not found for topic '%s'", topic), + Error: err.Error(), + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + } + c.JSON(http.StatusNotFound, er) + return + } + + for i := 0; i < len(obMap); i++ { + var license models.LicenseDB + licenseQuery := db.DB.Model(&license) + licenseQuery.Where(models.LicenseDB{Id: obMap[i].RfPk}).First(&license) + shortnameList = append(shortnameList, license.Shortname) + } + + resObMap = models.ObligationMapUser{ + Topic: topic, + Shortnames: shortnameList, + } + + res := models.ObligationMapResponse{ + Data: []models.ObligationMapUser{resObMap}, + Status: http.StatusOK, + Meta: models.PaginationMeta{ + ResourceCount: 1, + }, + } + c.JSON(http.StatusOK, res) +} + +// GetObligationMapByLicense retrieves obligation maps for given license shortname +// +// @Summary Get maps for a license +// @Description Get obligation maps for a given license shortname +// @Id GetObligationMapByLicense +// @Tags Obligations +// @Accept json +// @Produce json +// @Param license path string true "Shortname of the license" +// @Success 200 {object} models.ObligationMapResponse +// @Failure 404 {object} models.LicenseError "No license with given shortname found or no map for +// license exists" +// @Router /obligation_maps/license/{license} [get] +func GetObligationMapByLicense(c *gin.Context) { + var license models.LicenseDB + var obMap []models.ObligationMap + var resObMapList []models.ObligationMapUser + + licenseShortName := c.Param("license") + query := db.DB.Model(&license) + + if err := query.Where(models.LicenseDB{Shortname: licenseShortName}).First(&license).Error; err != nil { + er := models.LicenseError{ + Status: http.StatusNotFound, + Message: fmt.Sprintf("license with shortname '%s' not found", licenseShortName), + Error: err.Error(), + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + } + c.JSON(http.StatusNotFound, er) + return + } + + query = db.DB.Model(&obMap) + + if err := query.Where(models.ObligationMap{RfPk: license.Id}).Find(&obMap).Error; err != nil { + er := models.LicenseError{ + Status: http.StatusNotFound, + Message: fmt.Sprintf("Obligation map not found for license '%s'", licenseShortName), + Error: err.Error(), + Path: c.Request.URL.Path, + Timestamp: time.Now().Format(time.RFC3339), + } + c.JSON(http.StatusNotFound, er) + return + } + + for i := 0; i < len(obMap); i++ { + var obligation models.Obligation + obligationQuery := db.DB.Model(&obligation) + obligationQuery.Where(models.Obligation{Id: obMap[i].ObligationPk}).First(&obligation) + resObMapList = append(resObMapList, models.ObligationMapUser{ + Topic: obligation.Topic, + Shortnames: []string{licenseShortName}, + }) + } + + res := models.ObligationMapResponse{ + Data: resObMapList, + Status: http.StatusOK, + Meta: models.PaginationMeta{ + ResourceCount: len(resObMapList), + }, + } + c.JSON(http.StatusOK, res) +} diff --git a/pkg/models/types.go b/pkg/models/types.go index cc76dcb..ce5a76b 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -240,6 +240,13 @@ type UpdateObligation struct { TextUpdatable bool `json:"text_updatable"` } +// ObligationResponse represents the response format for obligation data. +type ObligationResponse struct { + Status int `json:"status" example:"200"` + Data []Obligation `json:"data"` + Meta PaginationMeta `json:"paginationmeta"` +} + // ObligationMap represents the mapping between an obligation and a license. type ObligationMap struct { ObligationPk int64 `json:"obligation_pk"` @@ -249,9 +256,15 @@ type ObligationMap struct { LicenseDB LicenseDB `gorm:"foreignKey:RfPk;references:Id" json:"-"` } -// ObligationResponse represents the response format for obligation data. -type ObligationResponse struct { - Status int `json:"status" example:"200"` - Data []Obligation `json:"data"` - Meta PaginationMeta `json:"paginationmeta"` +// ObligationMapUser Structure with obligation topic and license shortname list, a simple representation for user. +type ObligationMapUser struct { + Topic string `json:"topic" example:"copyleft"` + Shortnames []string `json:"shortnames" example:"GPL-2.0-only,GPL-2.0-or-later"` +} + +// ObligationMapResponse response format for obligation map data. +type ObligationMapResponse struct { + Status int `json:"status" example:"200"` + Data []ObligationMapUser `json:"data"` + Meta PaginationMeta `json:"paginationmeta"` }