Skip to content

Commit

Permalink
feat(obligation_import): Add functionality to import obligations via …
Browse files Browse the repository at this point in the history
…json file

Signed-off-by: deo002 <[email protected]>
  • Loading branch information
deo002 committed Apr 1, 2024
1 parent 13760df commit 0e20943
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 0 deletions.
64 changes: 64 additions & 0 deletions cmd/laas/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,56 @@ const docTemplate = `{
}
}
},
"/obligations/import": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Import obligations by uploading a json file",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"Obligations"
],
"summary": "Import obligations by uploading a json file",
"operationId": "ImportObligations",
"parameters": [
{
"type": "file",
"description": "obligations json file list",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"207": {
"description": "Multi-Status",
"schema": {
"$ref": "#/definitions/models.ImportObligationsResponse"
}
},
"400": {
"description": "input file must be present",
"schema": {
"$ref": "#/definitions/models.LicenseError"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/models.LicenseError"
}
}
}
}
},
"/obligations/{topic}": {
"get": {
"description": "Get an active based on given topic",
Expand Down Expand Up @@ -1356,6 +1406,20 @@ const docTemplate = `{
}
}
},
"models.ImportObligationsResponse": {
"type": "object",
"properties": {
"data": {
"description": "can be of type models.LicenseError or models.ObligationImportStatus",
"type": "array",
"items": {}
},
"status": {
"type": "integer",
"example": 207
}
}
},
"models.LicenseDB": {
"type": "object",
"required": [
Expand Down
64 changes: 64 additions & 0 deletions cmd/laas/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,56 @@
}
}
},
"/obligations/import": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Import obligations by uploading a json file",
"consumes": [
"multipart/form-data"
],
"produces": [
"application/json"
],
"tags": [
"Obligations"
],
"summary": "Import obligations by uploading a json file",
"operationId": "ImportObligations",
"parameters": [
{
"type": "file",
"description": "obligations json file list",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"207": {
"description": "Multi-Status",
"schema": {
"$ref": "#/definitions/models.ImportObligationsResponse"
}
},
"400": {
"description": "input file must be present",
"schema": {
"$ref": "#/definitions/models.LicenseError"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/models.LicenseError"
}
}
}
}
},
"/obligations/{topic}": {
"get": {
"description": "Get an active based on given topic",
Expand Down Expand Up @@ -1349,6 +1399,20 @@
}
}
},
"models.ImportObligationsResponse": {
"type": "object",
"properties": {
"data": {
"description": "can be of type models.LicenseError or models.ObligationImportStatus",
"type": "array",
"items": {}
},
"status": {
"type": "integer",
"example": 207
}
}
},
"models.LicenseDB": {
"type": "object",
"required": [
Expand Down
42 changes: 42 additions & 0 deletions cmd/laas/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ definitions:
example: 200
type: integer
type: object
models.ImportObligationsResponse:
properties:
data:
description: can be of type models.LicenseError or models.ObligationImportStatus
items: {}
type: array
status:
example: 207
type: integer
type: object
models.LicenseDB:
properties:
external_ref:
Expand Down Expand Up @@ -1231,6 +1241,38 @@ paths:
summary: Fetches audits corresponding to an obligation
tags:
- Obligations
/obligations/import:
post:
consumes:
- multipart/form-data
description: Import obligations by uploading a json file
operationId: ImportObligations
parameters:
- description: obligations json file list
in: formData
name: file
required: true
type: file
produces:
- application/json
responses:
"207":
description: Multi-Status
schema:
$ref: '#/definitions/models.ImportObligationsResponse'
"400":
description: input file must be present
schema:
$ref: '#/definitions/models.LicenseError'
"500":
description: Internal server error
schema:
$ref: '#/definitions/models.LicenseError'
security:
- ApiKeyAuth: []
summary: Import obligations by uploading a json file
tags:
- Obligations
/search:
post:
consumes:
Expand Down
1 change: 1 addition & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ func Router() *gin.Engine {
obligations := authorizedv1.Group("/obligations")
{
obligations.POST("", CreateObligation)
obligations.POST("import", ImportObligations)
obligations.PATCH(":topic", UpdateObligation)
obligations.DELETE(":topic", DeleteObligation)
}
Expand Down
149 changes: 149 additions & 0 deletions pkg/api/obligations.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ package api
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"path/filepath"
"strconv"
"time"

Expand Down Expand Up @@ -562,3 +565,149 @@ func GetObligationAudits(c *gin.Context) {

c.JSON(http.StatusOK, response)
}

// ImportObligations creates new obligation records via a json file.
//
// @Summary Import obligations by uploading a json file
// @Description Import obligations by uploading a json file
// @Id ImportObligations
// @Tags Obligations
// @Accept multipart/form-data
// @Produce json
// @Param file formData file true "obligations json file list"
// @Success 207 {object} models.ImportObligationsResponse
// @Failure 400 {object} models.LicenseError "input file must be present"
// @Failure 500 {object} models.LicenseError "Internal server error"
// @Security ApiKeyAuth
// @Router /obligations/import [post]
func ImportObligations(c *gin.Context) {
file, header, err := c.Request.FormFile("file")
if err != nil {
er := models.LicenseError{
Status: http.StatusBadRequest,
Message: "input file must be present",
Error: err.Error(),
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
}
c.JSON(http.StatusBadRequest, er)
return
}
defer file.Close()

if filepath.Ext(header.Filename) != ".json" {
er := models.LicenseError{
Status: http.StatusBadRequest,
Message: "only files with format *.json are allowed",
Error: "only files with format *.json are allowed",
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
}
c.JSON(http.StatusBadRequest, er)
return
}

var obligations []models.ObligationImport
decoder := json.NewDecoder(file)
if err := decoder.Decode(&obligations); err != nil {
er := models.LicenseError{
Status: http.StatusInternalServerError,
Message: "invalid json",
Error: err.Error(),
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
}
c.JSON(http.StatusInternalServerError, er)
return
}

res := models.ImportObligationsResponse{
Status: http.StatusMultiStatus,
}

for _, obligation := range obligations {
_ = db.DB.Transaction(func(tx *gorm.DB) error {
ob := models.Obligation{
Topic: obligation.Topic,
Type: obligation.Type,
Text: obligation.Text,
Classification: obligation.Classification,
Modifications: obligation.Modifications,
Comment: obligation.Comment,
Active: obligation.Active,
TextUpdatable: obligation.TextUpdatable,
}

hash := md5.Sum([]byte(ob.Text))
md5hash := hex.EncodeToString(hash[:])
ob.Md5 = md5hash

result := tx.
Where(&models.Obligation{Topic: ob.Topic}).
Or(&models.Obligation{Md5: ob.Md5}).
FirstOrCreate(&ob)

if result.RowsAffected == 0 {
res.Data = append(res.Data, models.LicenseError{
Status: http.StatusConflict,
Message: fmt.Sprintf("Obligation with topic '%s' or MD5 '%s' already exists",
ob.Topic, ob.Md5),
Error: ob.Topic,
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
})
return errors.New("obligation already exists")
}
if result.Error != nil {
res.Data = append(res.Data, models.LicenseError{
Status: http.StatusInternalServerError,
Message: fmt.Sprintf("Failed to create obligation: %s", result.Error.Error()),
Error: ob.Topic,
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
})
return err
}

var maps []models.ObligationMap
for _, i := range obligation.Shortnames {
var license models.LicenseDB
if err := tx.Debug().Where(models.LicenseDB{Shortname: i}).First(&license).Error; err != nil {
res.Data = append(res.Data, models.LicenseError{
Status: http.StatusInternalServerError,
Message: fmt.Sprintf("Error finding license with shortname: %s", i),
Error: ob.Topic,
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
})
return err
}
log.Println(license.Shortname)
maps = append(maps, models.ObligationMap{
ObligationPk: ob.Id,
RfPk: license.Id,
})
}

if err := tx.Create(&maps).Error; err != nil {
res.Data = append(res.Data, models.LicenseError{
Status: http.StatusInternalServerError,
Message: "Error linking obligation with license",
Error: ob.Topic,
Path: c.Request.URL.Path,
Timestamp: time.Now().Format(time.RFC3339),
})
return err
}

res.Data = append(res.Data, models.ObligationImportStatus{
Data: models.ObligationId{Id: ob.Id, Topic: ob.Topic},
Status: http.StatusCreated,
})

return nil
})
}

c.JSON(http.StatusMultiStatus, res)
}
Loading

0 comments on commit 0e20943

Please sign in to comment.