adding ProblemDetail model for error message 57/14257/16
authorrajdeep11 <rajdeep.sin@samsung.com>
Thu, 13 Mar 2025 10:50:53 +0000 (16:20 +0530)
committerRajdeep Singh <rajdeep.sin@samsung.com>
Thu, 15 May 2025 10:04:17 +0000 (10:04 +0000)
Issue-Id: AIMLFW-181

1) Added the Problem detail model.
2) changes to the register model info.

Change-Id: I0c107003676e95a31f17a64501d8d489fd5ca4e1
Signed-off-by: rajdeep11 <rajdeep.sin@samsung.com>
apis/mmes_apis.go
apis_test/mmes_apis_test.go
go.mod
models/ProblemDetail.go [new file with mode: 0644]

index 183d5ad..2807bcc 100644 (file)
@@ -22,6 +22,7 @@ import (
        "io"
        "net/http"
        "os"
+       "errors"
 
        "gerrit.o-ran-sc.org/r/aiml-fw/awmf/modelmgmtservice/core"
        "gerrit.o-ran-sc.org/r/aiml-fw/awmf/modelmgmtservice/db"
@@ -30,6 +31,8 @@ import (
        "github.com/gin-gonic/gin"
        "github.com/go-playground/validator/v10"
        "github.com/google/uuid"
+       "github.com/jackc/pgerrcode"
+       "github.com/lib/pq"
 )
 
 const (
@@ -51,12 +54,15 @@ func NewMmeApiHandler(dbMgr core.DBMgr, iDB db.IDB) *MmeApiHandler {
 }
 
 func (m *MmeApiHandler) RegisterModel(cont *gin.Context) {
+       logging.INFO("registering model info")
 
        var modelInfo models.ModelRelatedInformation
 
        if err := cont.ShouldBindJSON(&modelInfo); err != nil {
-               cont.JSON(http.StatusBadRequest, gin.H{
-                       "error": err.Error(),
+               cont.JSON(http.StatusBadRequest, models.ProblemDetail{
+                       Status: http.StatusBadRequest,
+                       Title:  "Bad Request",
+                       Detail: fmt.Sprintf("The request json is not correct, %s", err.Error()),
                })
                return
        }
@@ -66,8 +72,10 @@ func (m *MmeApiHandler) RegisterModel(cont *gin.Context) {
 
        validate := validator.New()
        if err := validate.Struct(modelInfo); err != nil {
-               cont.JSON(http.StatusBadRequest, gin.H{
-                       "error": err.Error(),
+               cont.JSON(http.StatusBadRequest, models.ProblemDetail{
+                       Status: http.StatusBadRequest,
+                       Title:  "Bad Request",
+                       Detail: fmt.Sprintf("The request json is not correct as it can't be validated, %s", err.Error()),
                })
                return
        }
@@ -76,10 +84,24 @@ func (m *MmeApiHandler) RegisterModel(cont *gin.Context) {
 
        if err := m.iDB.Create(modelInfo); err != nil {
                logging.ERROR("error", err)
-               cont.JSON(http.StatusBadRequest, gin.H{
-                       "Error": err.Error(),
-               })
-               return
+               var pqErr *pq.Error
+               if errors.As(err, &pqErr) {
+                       if pqErr.Code == pgerrcode.UniqueViolation {
+                               cont.JSON(http.StatusConflict, models.ProblemDetail{
+                                       Status: http.StatusConflict,
+                                       Title:  "model name and version combination already present",
+                                       Detail: "The request json is not correct as",
+                               })
+                               return
+                       } else {
+                               cont.JSON(http.StatusInternalServerError, models.ProblemDetail{
+                                       Status: http.StatusInternalServerError,
+                                       Title:  "Bad Request",
+                                       Detail: "The request json is not correct as",
+                               })
+                               return
+                       }
+               }
        }
 
        logging.INFO("model is saved.")
@@ -88,36 +110,7 @@ func (m *MmeApiHandler) RegisterModel(cont *gin.Context) {
                "modelInfo": modelInfo,
        })
 
-       // logging.INFO("Creating model...")
-       // bodyBytes, _ := io.ReadAll(cont.Request.Body)
-
-       // var modelInfo models.ModelInfo
-       // //Need to unmarshal JSON to Struct, to access request
-       // //data such as model name, rapp id etc
-       // err := json.Unmarshal(bodyBytes, &modelInfo)
-       // if err != nil || modelInfo.ModelId.ModelName == "" {
-       //      logging.ERROR("Error in unmarshalling")
-       //      cont.JSON(http.StatusBadRequest, gin.H{
-       //              "code":    http.StatusBadRequest,
-       //              "message": string("Can not parse input data, provide mandatory details"),
-       //      })
-       // } else {
-       //      id := uuid.New()
-       //      modelInfo.Id = id.String()
-       //      modelInfoBytes, _ := json.Marshal(modelInfo)
-       //      err := m.dbmgr.CreateBucket(modelInfo.ModelId.ModelName)
-       //      if err == nil {
-       //              m.dbmgr.UploadFile(modelInfoBytes, modelInfo.ModelId.ModelName+os.Getenv("INFO_FILE_POSTFIX"), modelInfo.ModelId.ModelName)
-       //      } else {
-       //              cont.JSON(http.StatusInternalServerError, gin.H{
-       //                      "code":    http.StatusInternalServerError,
-       //                      "message": err.Error(),
-       //              })
-       //      }
-       //      cont.JSON(http.StatusCreated, gin.H{
-       //              "modelinfo": modelInfoBytes,
-       //      })
-       // }
+
 }
 
 /*
index 25a643a..2c6a268 100644 (file)
@@ -35,6 +35,8 @@ import (
        "gerrit.o-ran-sc.org/r/aiml-fw/awmf/modelmgmtservice/routers"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/mock"
+       "github.com/jackc/pgerrcode"
+       "github.com/lib/pq"
 )
 
 var registerModelBody = `{
@@ -53,6 +55,38 @@ var registerModelBody = `{
     }
 }`
 
+var invalidRegisterModelBody = `{
+       "id" : "id",
+    "modelId": {
+        "modelName": "model3",
+        "modelVersion" : "2"
+    },
+    "description": "hello world2",
+    "modelInformation": {
+        "metadata": {
+            "author": "someone"
+        },
+        "inputDataType": "pdcpBytesDl,pdcpBytesUl,kpi",
+        "outputDataType": "c, d"
+    }
+`
+
+var invalidRegisterModelBody2 = `{
+       "id" : "id",
+    "modelId": {
+        "modelName": "model3",
+        "modelsion" : "2"
+    },
+    "description": "hello world2",
+    "modelInformation": {
+        "metadata": {
+            "author": "someone"
+        },
+        "inputDataType": "pdcpBytesDl,pdcpBytesUl,kpi",
+        "outputDataType": "c, d"
+    }
+}`
+
 type dbMgrMock struct {
        mock.Mock
        core.DBMgr
@@ -111,6 +145,68 @@ func TestRegisterModel(t *testing.T) {
        assert.Equal(t, 201, w.Code)
 }
 
+func TestRegisterModelFailInvalidJson(t *testing.T) {
+       os.Setenv("LOG_FILE_NAME", "testing")
+       iDBMockInst := new(iDBMock)
+       handler := apis.NewMmeApiHandler(nil, iDBMockInst)
+       router := routers.InitRouter(handler)
+       w := httptest.NewRecorder()
+       req, _ := http.NewRequest("POST", "/ai-ml-model-registration/v1/model-registrations", strings.NewReader(invalidRegisterModelBody))
+       router.ServeHTTP(w, req)
+
+       assert.Equal(t, 400, w.Code)
+       body, _ := io.ReadAll(w.Body)
+
+       assert.Equal(t, `{"status":400,"title":"Bad Request","detail":"The request json is not correct, unexpected EOF"}`, string(body))
+}
+
+func TestRegisterModelFailInvalidRequest(t *testing.T) {
+       os.Setenv("LOG_FILE_NAME", "testing")
+       iDBMockInst := new(iDBMock)
+       handler := apis.NewMmeApiHandler(nil, iDBMockInst)
+       router := routers.InitRouter(handler)
+       w := httptest.NewRecorder()
+       req, _ := http.NewRequest("POST", "/ai-ml-model-registration/v1/model-registrations", strings.NewReader(invalidRegisterModelBody2))
+       router.ServeHTTP(w, req)
+
+       assert.Equal(t, 400, w.Code)
+       body, _ := io.ReadAll(w.Body)
+
+       assert.Equal(t, `{"status":400,"title":"Bad Request","detail":"The request json is not correct as it can't be validated, Key: 'ModelRelatedInformation.ModelId.ModelVersion' Error:Field validation for 'ModelVersion' failed on the 'required' tag"}`, string(body))
+}
+
+func TestRegisterModelFailCreateDuplicateModel(t *testing.T) {
+       os.Setenv("LOG_FILE_NAME", "testing")
+       iDBMockInst := new(iDBMock)
+       iDBMockInst.On("Create", mock.Anything).Return(&pq.Error{Code: pgerrcode.UniqueViolation})
+       handler := apis.NewMmeApiHandler(nil, iDBMockInst)
+       router := routers.InitRouter(handler)
+       w := httptest.NewRecorder()
+       req, _ := http.NewRequest("POST", "/ai-ml-model-registration/v1/model-registrations", strings.NewReader(registerModelBody))
+       router.ServeHTTP(w, req)
+
+       assert.Equal(t, 409, w.Code)
+       body, _ := io.ReadAll(w.Body)
+
+       assert.Equal(t, "{\"status\":409,\"title\":\"model name and version combination already present\",\"detail\":\"The request json is not correct as\"}", string(body))
+}
+
+func TestRegisterModelFailCreate(t *testing.T) {
+       os.Setenv("LOG_FILE_NAME", "testing")
+       iDBMockInst := new(iDBMock)
+       iDBMockInst.On("Create", mock.Anything).Return(&pq.Error{Code: pgerrcode.SQLClientUnableToEstablishSQLConnection})
+       handler := apis.NewMmeApiHandler(nil, iDBMockInst)
+       router := routers.InitRouter(handler)
+       w := httptest.NewRecorder()
+       req, _ := http.NewRequest("POST", "/ai-ml-model-registration/v1/model-registrations", strings.NewReader(registerModelBody))
+       router.ServeHTTP(w, req)
+
+       assert.Equal(t, 500, w.Code)
+       body, _ := io.ReadAll(w.Body)
+
+       assert.Equal(t, "{\"status\":500,\"title\":\"Bad Request\",\"detail\":\"The request json is not correct as\"}", string(body))
+}
+
 func TestWhenSuccessGetModelInfoList(t *testing.T) {
        // Setting ENV
        os.Setenv("LOG_FILE_NAME", "testing")
diff --git a/go.mod b/go.mod
index 17fd0a6..787197a 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,8 @@ require (
        gopkg.in/natefinch/lumberjack.v2 v2.2.1
        gorm.io/driver/postgres v1.5.9
        gorm.io/gorm v1.25.12
+    github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
+       github.com/lib/pq v1.10.7
 )
 
 require (
diff --git a/models/ProblemDetail.go b/models/ProblemDetail.go
new file mode 100644 (file)
index 0000000..2655531
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+==================================================================================
+Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==================================================================================
+*/
+
+package models
+
+type ProblemDetail struct{
+       Status int `json:"status"`
+       Title string `json:"title"`
+       Detail string `json:"detail"`
+}
+