"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"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
+ "github.com/jackc/pgerrcode"
+ "github.com/lib/pq"
)
const (
}
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
}
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
}
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.")
"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,
- // })
- // }
+
}
/*
"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 = `{
}
}`
+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
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")
--- /dev/null
+/*
+==================================================================================
+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"`
+}
+