feat: use db as source in model catalog (#1667)
* feat: switch to db usage WIP Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: partially fix model_catalog_service tests Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: tests Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: change mustache template to support array of primitive type plus reflect removal Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: /models endpoint not working Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: artifacts route 500 status code Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * feat: make artifacts run on a unified repository Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * feat: add custom properties to catalog artifacts Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * feat: add tests files to db_catalog and ctalog_artifact Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: pagination issues Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: return 404 is the model does not exist in the Get model Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: 500 error code on bad user input Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * fix: q parameter not working Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> * chore: better function/alias naming Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> Co-authored-by: Paul Boyd <paul@pboyd.io> --------- Signed-off-by: Alessio Pragliola <seth.pro@gmail.com> Co-authored-by: Paul Boyd <paul@pboyd.io>
This commit is contained in:
parent
421742f2ea
commit
ad0d5f857b
|
|
@ -20,12 +20,18 @@ paths:
|
|||
parameters:
|
||||
- name: source
|
||||
description: |-
|
||||
Filter models by source. This parameter is currently required and
|
||||
may only be specified once.
|
||||
Filter models by source. This parameter can be specified multiple times
|
||||
to filter by multiple sources (OR logic). For example:
|
||||
?source=huggingface&source=local will return models from either
|
||||
huggingface OR local sources.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
style: form
|
||||
explode: true
|
||||
in: query
|
||||
required: true
|
||||
required: false
|
||||
- name: q
|
||||
description: Free-form keyword search used to filter the response.
|
||||
schema:
|
||||
|
|
@ -135,6 +141,10 @@ paths:
|
|||
type: string
|
||||
in: path
|
||||
required: true
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
- $ref: "#/components/parameters/orderBy"
|
||||
- $ref: "#/components/parameters/sortOrder"
|
||||
- $ref: "#/components/parameters/nextPageToken"
|
||||
components:
|
||||
schemas:
|
||||
ArtifactTypeQueryParam:
|
||||
|
|
@ -202,6 +212,35 @@ components:
|
|||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
BaseResource:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
customProperties:
|
||||
description: User provided custom properties which are not defined by its type.
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
description:
|
||||
description: |-
|
||||
An optional description about the resource.
|
||||
type: string
|
||||
externalId:
|
||||
description: |-
|
||||
The external id that come from the clients’ system. This field is optional.
|
||||
If set, it must be unique among all resources within a database instance.
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
The client provided name of the artifact. This field is optional. If set,
|
||||
it must be unique among all the artifacts of the same artifact type within
|
||||
a database instance and cannot be changed once set.
|
||||
type: string
|
||||
id:
|
||||
format: int64
|
||||
description: The unique server generated id of the resource.
|
||||
type: string
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
BaseResourceDates:
|
||||
description: Common timestamp fields for resources
|
||||
type: object
|
||||
|
|
@ -278,7 +317,7 @@ components:
|
|||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModel:
|
||||
description: A model in the model catalog.
|
||||
allOf:
|
||||
|
|
@ -293,8 +332,8 @@ components:
|
|||
source_id:
|
||||
type: string
|
||||
description: ID of the source this model belongs to.
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseModel"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModelArtifact:
|
||||
description: A Catalog Model Artifact Entity.
|
||||
allOf:
|
||||
|
|
@ -315,7 +354,7 @@ components:
|
|||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModelList:
|
||||
description: List of CatalogModel entities.
|
||||
allOf:
|
||||
|
|
|
|||
|
|
@ -20,12 +20,18 @@ paths:
|
|||
parameters:
|
||||
- name: source
|
||||
description: |-
|
||||
Filter models by source. This parameter is currently required and
|
||||
may only be specified once.
|
||||
Filter models by source. This parameter can be specified multiple times
|
||||
to filter by multiple sources (OR logic). For example:
|
||||
?source=huggingface&source=local will return models from either
|
||||
huggingface OR local sources.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
style: form
|
||||
explode: true
|
||||
in: query
|
||||
required: true
|
||||
required: false
|
||||
- name: q
|
||||
description: Free-form keyword search used to filter the response.
|
||||
schema:
|
||||
|
|
@ -135,6 +141,10 @@ paths:
|
|||
type: string
|
||||
in: path
|
||||
required: true
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
- $ref: "#/components/parameters/orderBy"
|
||||
- $ref: "#/components/parameters/sortOrder"
|
||||
- $ref: "#/components/parameters/nextPageToken"
|
||||
components:
|
||||
schemas:
|
||||
CatalogArtifact:
|
||||
|
|
@ -181,7 +191,7 @@ components:
|
|||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModel:
|
||||
description: A model in the model catalog.
|
||||
allOf:
|
||||
|
|
@ -196,8 +206,8 @@ components:
|
|||
source_id:
|
||||
type: string
|
||||
description: ID of the source this model belongs to.
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseModel"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModelArtifact:
|
||||
description: A Catalog Model Artifact Entity.
|
||||
allOf:
|
||||
|
|
@ -218,7 +228,7 @@ components:
|
|||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
- $ref: "#/components/schemas/BaseResource"
|
||||
CatalogModelList:
|
||||
description: List of CatalogModel entities.
|
||||
allOf:
|
||||
|
|
|
|||
|
|
@ -79,6 +79,35 @@ components:
|
|||
description: Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
type: string
|
||||
readOnly: true
|
||||
BaseResource:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
customProperties:
|
||||
description: User provided custom properties which are not defined by its type.
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
description:
|
||||
description: |-
|
||||
An optional description about the resource.
|
||||
type: string
|
||||
externalId:
|
||||
description: |-
|
||||
The external id that come from the clients’ system. This field is optional.
|
||||
If set, it must be unique among all resources within a database instance.
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
The client provided name of the artifact. This field is optional. If set,
|
||||
it must be unique among all the artifacts of the same artifact type within
|
||||
a database instance and cannot be changed once set.
|
||||
type: string
|
||||
id:
|
||||
format: int64
|
||||
description: The unique server generated id of the resource.
|
||||
type: string
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
BaseResourceList:
|
||||
required:
|
||||
- nextPageToken
|
||||
|
|
|
|||
|
|
@ -1676,35 +1676,6 @@ components:
|
|||
dataset-artifact: "#/components/schemas/DataSetUpdate"
|
||||
metric: "#/components/schemas/MetricUpdate"
|
||||
parameter: "#/components/schemas/ParameterUpdate"
|
||||
BaseResource:
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
customProperties:
|
||||
description: User provided custom properties which are not defined by its type.
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/MetadataValue"
|
||||
description:
|
||||
description: |-
|
||||
An optional description about the resource.
|
||||
type: string
|
||||
externalId:
|
||||
description: |-
|
||||
The external id that come from the clients’ system. This field is optional.
|
||||
If set, it must be unique among all resources within a database instance.
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
The client provided name of the artifact. This field is optional. If set,
|
||||
it must be unique among all the artifacts of the same artifact type within
|
||||
a database instance and cannot be changed once set.
|
||||
type: string
|
||||
id:
|
||||
format: int64
|
||||
description: The unique server generated id of the resource.
|
||||
type: string
|
||||
- $ref: "#/components/schemas/BaseResourceDates"
|
||||
BaseResourceCreate:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/catalog"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/service"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/server/openapi"
|
||||
"github.com/kubeflow/model-registry/internal/datastore"
|
||||
|
|
@ -46,7 +48,7 @@ func runCatalogServer(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("error creating datastore: %w", err)
|
||||
}
|
||||
|
||||
_, err = ds.Connect(service.DatastoreSpec())
|
||||
repoSet, err := ds.Connect(service.DatastoreSpec())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing datastore: %v", err)
|
||||
}
|
||||
|
|
@ -56,9 +58,21 @@ func runCatalogServer(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("error loading catalog sources: %v", err)
|
||||
}
|
||||
|
||||
svc := openapi.NewModelCatalogServiceAPIService(sources)
|
||||
svc := openapi.NewModelCatalogServiceAPIService(catalog.NewDBCatalog(
|
||||
getRepo[models.CatalogModelRepository](repoSet),
|
||||
getRepo[models.CatalogArtifactRepository](repoSet),
|
||||
), sources)
|
||||
ctrl := openapi.NewModelCatalogServiceAPIController(svc)
|
||||
|
||||
glog.Infof("Catalog API server listening on %s", catalogCfg.ListenAddress)
|
||||
return http.ListenAndServe(catalogCfg.ListenAddress, openapi.NewRouter(ctrl))
|
||||
}
|
||||
|
||||
func getRepo[T any](repoSet datastore.RepoSet) T {
|
||||
repo, err := repoSet.Repository(reflect.TypeFor[T]())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to get repository: %v", err))
|
||||
}
|
||||
|
||||
return repo.(T)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,18 @@ import (
|
|||
|
||||
type ListModelsParams struct {
|
||||
Query string
|
||||
SourceIDs []string
|
||||
PageSize int32
|
||||
OrderBy model.OrderByField
|
||||
SortOrder model.SortOrder
|
||||
NextPageToken *string
|
||||
}
|
||||
|
||||
type ListArtifactsParams struct {
|
||||
PageSize int32
|
||||
OrderBy model.OrderByField
|
||||
SortOrder model.SortOrder
|
||||
NextPageToken *string
|
||||
}
|
||||
|
||||
// CatalogSourceProvider is implemented by catalog source types, e.g. YamlCatalog
|
||||
|
|
@ -24,16 +34,17 @@ type CatalogSourceProvider interface {
|
|||
// GetModel returns model metadata for a single model by its name. If
|
||||
// nothing is found with the name provided it returns nil, without an
|
||||
// error.
|
||||
GetModel(ctx context.Context, name string) (*model.CatalogModel, error)
|
||||
GetModel(ctx context.Context, modelName string, sourceID string) (*model.CatalogModel, error)
|
||||
|
||||
// ListModels returns all models according to the parameters. If
|
||||
// nothing suitable is found, it returns an empty list.
|
||||
// If sourceIDs is provided, filter models by source IDs. If not provided, return all models.
|
||||
ListModels(ctx context.Context, params ListModelsParams) (model.CatalogModelList, error)
|
||||
|
||||
// GetArtifacts returns all artifacts for a particular model. If no
|
||||
// model is found with that name, it returns nil. If the model is
|
||||
// found, but has no artifacts, an empty list is returned.
|
||||
GetArtifacts(ctx context.Context, name string) (*model.CatalogArtifactList, error)
|
||||
GetArtifacts(ctx context.Context, modelName string, sourceID string, params ListArtifactsParams) (model.CatalogArtifactList, error)
|
||||
}
|
||||
|
||||
// CatalogSourceConfig is a single entry from the catalog sources YAML file.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,415 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
dbmodels "github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
apimodels "github.com/kubeflow/model-registry/catalog/pkg/openapi"
|
||||
"github.com/kubeflow/model-registry/internal/converter"
|
||||
mrmodels "github.com/kubeflow/model-registry/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/pkg/api"
|
||||
"github.com/kubeflow/model-registry/pkg/openapi"
|
||||
)
|
||||
|
||||
type dbCatalogImpl struct {
|
||||
catalogModelRepository dbmodels.CatalogModelRepository
|
||||
catalogArtifactRepository dbmodels.CatalogArtifactRepository
|
||||
}
|
||||
|
||||
func NewDBCatalog(
|
||||
catalogModelRepository dbmodels.CatalogModelRepository,
|
||||
catalogArtifactRepository dbmodels.CatalogArtifactRepository,
|
||||
) CatalogSourceProvider {
|
||||
return &dbCatalogImpl{
|
||||
catalogModelRepository: catalogModelRepository,
|
||||
catalogArtifactRepository: catalogArtifactRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dbCatalogImpl) GetModel(ctx context.Context, modelName string, sourceID string) (*apimodels.CatalogModel, error) {
|
||||
modelsList, err := d.catalogModelRepository.List(dbmodels.CatalogModelListOptions{
|
||||
Name: &modelName,
|
||||
SourceIDs: &[]string{sourceID},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(modelsList.Items) == 0 {
|
||||
return nil, fmt.Errorf("no models found for name=%v: %w", modelName, api.ErrNotFound)
|
||||
}
|
||||
|
||||
if len(modelsList.Items) > 1 {
|
||||
return nil, fmt.Errorf("multiple models found for name=%v: %w", modelName, api.ErrNotFound)
|
||||
}
|
||||
|
||||
model := mapDBModelToAPIModel(modelsList.Items[0])
|
||||
|
||||
return &model, nil
|
||||
}
|
||||
|
||||
func (d *dbCatalogImpl) ListModels(ctx context.Context, params ListModelsParams) (apimodels.CatalogModelList, error) {
|
||||
pageSize := int32(params.PageSize)
|
||||
|
||||
// Use consistent defaults to match pagination logic
|
||||
orderBy := string(params.OrderBy)
|
||||
if orderBy == "" {
|
||||
orderBy = mrmodels.DefaultOrderBy
|
||||
}
|
||||
|
||||
sortOrder := string(params.SortOrder)
|
||||
if sortOrder == "" {
|
||||
sortOrder = mrmodels.DefaultSortOrder
|
||||
}
|
||||
|
||||
nextPageToken := params.NextPageToken
|
||||
|
||||
var queryPtr *string
|
||||
if params.Query != "" {
|
||||
queryPtr = ¶ms.Query
|
||||
}
|
||||
|
||||
modelsList, err := d.catalogModelRepository.List(dbmodels.CatalogModelListOptions{
|
||||
SourceIDs: ¶ms.SourceIDs,
|
||||
Query: queryPtr,
|
||||
Pagination: mrmodels.Pagination{
|
||||
PageSize: &pageSize,
|
||||
OrderBy: &orderBy,
|
||||
SortOrder: &sortOrder,
|
||||
NextPageToken: nextPageToken,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return apimodels.CatalogModelList{}, err
|
||||
}
|
||||
|
||||
modelList := &apimodels.CatalogModelList{
|
||||
Items: make([]apimodels.CatalogModel, 0),
|
||||
}
|
||||
|
||||
for _, model := range modelsList.Items {
|
||||
modelList.Items = append(modelList.Items, mapDBModelToAPIModel(model))
|
||||
}
|
||||
|
||||
modelList.NextPageToken = modelsList.NextPageToken
|
||||
modelList.PageSize = pageSize
|
||||
modelList.Size = int32(len(modelsList.Items))
|
||||
|
||||
return *modelList, nil
|
||||
}
|
||||
|
||||
func (d *dbCatalogImpl) GetArtifacts(ctx context.Context, modelName string, sourceID string, params ListArtifactsParams) (apimodels.CatalogArtifactList, error) {
|
||||
pageSize := int32(params.PageSize)
|
||||
|
||||
// Use consistent defaults to match pagination logic
|
||||
orderBy := string(params.OrderBy)
|
||||
if orderBy == "" {
|
||||
orderBy = mrmodels.DefaultOrderBy
|
||||
}
|
||||
|
||||
sortOrder := string(params.SortOrder)
|
||||
if sortOrder == "" {
|
||||
sortOrder = mrmodels.DefaultSortOrder
|
||||
}
|
||||
|
||||
nextPageToken := params.NextPageToken
|
||||
|
||||
m, err := d.GetModel(ctx, modelName, sourceID)
|
||||
if err != nil {
|
||||
if errors.Is(err, api.ErrNotFound) {
|
||||
return apimodels.CatalogArtifactList{}, fmt.Errorf("invalid model name '%s' for source '%s': %w", modelName, sourceID, api.ErrBadRequest)
|
||||
}
|
||||
return apimodels.CatalogArtifactList{}, err
|
||||
}
|
||||
|
||||
parentResourceID, err := strconv.ParseInt(*m.Id, 10, 32)
|
||||
if err != nil {
|
||||
return apimodels.CatalogArtifactList{}, err
|
||||
}
|
||||
|
||||
parentResourceID32 := int32(parentResourceID)
|
||||
|
||||
artifactsList, err := d.catalogArtifactRepository.List(dbmodels.CatalogArtifactListOptions{
|
||||
ParentResourceID: &parentResourceID32,
|
||||
Pagination: mrmodels.Pagination{
|
||||
PageSize: &pageSize,
|
||||
OrderBy: &orderBy,
|
||||
SortOrder: &sortOrder,
|
||||
NextPageToken: nextPageToken,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return apimodels.CatalogArtifactList{}, err
|
||||
}
|
||||
|
||||
artifactList := &apimodels.CatalogArtifactList{
|
||||
Items: make([]apimodels.CatalogArtifact, 0),
|
||||
}
|
||||
|
||||
for _, artifact := range artifactsList.Items {
|
||||
mappedArtifact, err := mapDBArtifactToAPIArtifact(artifact)
|
||||
if err != nil {
|
||||
return apimodels.CatalogArtifactList{}, err
|
||||
}
|
||||
artifactList.Items = append(artifactList.Items, mappedArtifact)
|
||||
}
|
||||
|
||||
artifactList.NextPageToken = artifactsList.NextPageToken
|
||||
artifactList.PageSize = pageSize
|
||||
artifactList.Size = int32(len(artifactList.Items))
|
||||
|
||||
return *artifactList, nil
|
||||
}
|
||||
|
||||
func mapDBModelToAPIModel(m dbmodels.CatalogModel) apimodels.CatalogModel {
|
||||
res := apimodels.CatalogModel{}
|
||||
|
||||
id := strconv.FormatInt(int64(*m.GetID()), 10)
|
||||
res.Id = &id
|
||||
|
||||
if m.GetAttributes() != nil {
|
||||
res.Name = *m.GetAttributes().Name
|
||||
res.ExternalId = m.GetAttributes().ExternalID
|
||||
|
||||
if m.GetAttributes().CreateTimeSinceEpoch != nil {
|
||||
createTimeSinceEpoch := strconv.FormatInt(*m.GetAttributes().CreateTimeSinceEpoch, 10)
|
||||
res.CreateTimeSinceEpoch = &createTimeSinceEpoch
|
||||
}
|
||||
if m.GetAttributes().LastUpdateTimeSinceEpoch != nil {
|
||||
lastUpdateTimeSinceEpoch := strconv.FormatInt(*m.GetAttributes().LastUpdateTimeSinceEpoch, 10)
|
||||
res.LastUpdateTimeSinceEpoch = &lastUpdateTimeSinceEpoch
|
||||
}
|
||||
}
|
||||
|
||||
if m.GetProperties() != nil {
|
||||
for _, prop := range *m.GetProperties() {
|
||||
switch prop.Name {
|
||||
case "source_id":
|
||||
if prop.StringValue != nil {
|
||||
res.SourceId = prop.StringValue
|
||||
}
|
||||
case "description":
|
||||
if prop.StringValue != nil {
|
||||
res.Description = prop.StringValue
|
||||
}
|
||||
case "library_name":
|
||||
if prop.StringValue != nil {
|
||||
res.LibraryName = prop.StringValue
|
||||
}
|
||||
case "license_link":
|
||||
if prop.StringValue != nil {
|
||||
res.LicenseLink = prop.StringValue
|
||||
}
|
||||
case "license":
|
||||
if prop.StringValue != nil {
|
||||
res.License = prop.StringValue
|
||||
}
|
||||
case "logo":
|
||||
if prop.StringValue != nil {
|
||||
res.Logo = prop.StringValue
|
||||
}
|
||||
case "maturity":
|
||||
if prop.StringValue != nil {
|
||||
res.Maturity = prop.StringValue
|
||||
}
|
||||
case "provider":
|
||||
if prop.StringValue != nil {
|
||||
res.Provider = prop.StringValue
|
||||
}
|
||||
case "readme":
|
||||
if prop.StringValue != nil {
|
||||
res.Readme = prop.StringValue
|
||||
}
|
||||
case "language":
|
||||
if prop.StringValue != nil {
|
||||
var languages []string
|
||||
if err := json.Unmarshal([]byte(*prop.StringValue), &languages); err == nil {
|
||||
res.Language = languages
|
||||
}
|
||||
}
|
||||
case "tasks":
|
||||
if prop.StringValue != nil {
|
||||
var tasks []string
|
||||
if err := json.Unmarshal([]byte(*prop.StringValue), &tasks); err == nil {
|
||||
res.Tasks = tasks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func mapDBArtifactToAPIArtifact(a dbmodels.CatalogArtifact) (apimodels.CatalogArtifact, error) {
|
||||
if a.CatalogModelArtifact != nil {
|
||||
return mapToModelArtifact(*a.CatalogModelArtifact)
|
||||
} else if a.CatalogMetricsArtifact != nil {
|
||||
metricsTypeValue := string((*a.CatalogMetricsArtifact).GetAttributes().MetricsType)
|
||||
return mapToMetricsArtifact(*a.CatalogMetricsArtifact, metricsTypeValue)
|
||||
}
|
||||
|
||||
return apimodels.CatalogArtifact{}, fmt.Errorf("invalid catalog artifact type: %v", a)
|
||||
}
|
||||
|
||||
func mapToModelArtifact(a dbmodels.CatalogModelArtifact) (apimodels.CatalogArtifact, error) {
|
||||
catalogModelArtifact := &apimodels.CatalogModelArtifact{
|
||||
ArtifactType: dbmodels.CatalogModelArtifactType,
|
||||
}
|
||||
|
||||
if a.GetID() != nil {
|
||||
id := strconv.FormatInt(int64(*a.GetID()), 10)
|
||||
catalogModelArtifact.Id = &id
|
||||
}
|
||||
|
||||
if a.GetAttributes() != nil {
|
||||
attrs := a.GetAttributes()
|
||||
|
||||
catalogModelArtifact.Name = attrs.Name
|
||||
catalogModelArtifact.ExternalId = attrs.ExternalID
|
||||
|
||||
if attrs.URI != nil {
|
||||
catalogModelArtifact.Uri = *attrs.URI
|
||||
}
|
||||
|
||||
if attrs.CreateTimeSinceEpoch != nil {
|
||||
createTime := strconv.FormatInt(*attrs.CreateTimeSinceEpoch, 10)
|
||||
catalogModelArtifact.CreateTimeSinceEpoch = &createTime
|
||||
}
|
||||
|
||||
if attrs.LastUpdateTimeSinceEpoch != nil {
|
||||
updateTime := strconv.FormatInt(*attrs.LastUpdateTimeSinceEpoch, 10)
|
||||
catalogModelArtifact.LastUpdateTimeSinceEpoch = &updateTime
|
||||
}
|
||||
}
|
||||
|
||||
if a.GetProperties() != nil {
|
||||
for _, prop := range *a.GetProperties() {
|
||||
switch prop.Name {
|
||||
case "description":
|
||||
if prop.StringValue != nil {
|
||||
catalogModelArtifact.Description = prop.StringValue
|
||||
}
|
||||
case "artifactType":
|
||||
if prop.StringValue != nil {
|
||||
catalogModelArtifact.ArtifactType = *prop.StringValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map custom properties
|
||||
if a.GetCustomProperties() != nil && len(*a.GetCustomProperties()) > 0 {
|
||||
customPropsMap, err := converter.MapEmbedMDCustomProperties(*a.GetCustomProperties())
|
||||
if err != nil {
|
||||
return apimodels.CatalogArtifact{}, fmt.Errorf("error mapping custom properties: %w", err)
|
||||
}
|
||||
|
||||
catalogCustomProps := convertMetadataValueMap(customPropsMap)
|
||||
catalogModelArtifact.CustomProperties = &catalogCustomProps
|
||||
}
|
||||
|
||||
return apimodels.CatalogArtifact{
|
||||
CatalogModelArtifact: catalogModelArtifact,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapToMetricsArtifact(a dbmodels.CatalogMetricsArtifact, metricsType string) (apimodels.CatalogArtifact, error) {
|
||||
catalogMetricsArtifact := &apimodels.CatalogMetricsArtifact{
|
||||
ArtifactType: dbmodels.CatalogMetricsArtifactType,
|
||||
MetricsType: metricsType,
|
||||
}
|
||||
|
||||
if a.GetID() != nil {
|
||||
id := strconv.FormatInt(int64(*a.GetID()), 10)
|
||||
catalogMetricsArtifact.Id = &id
|
||||
}
|
||||
|
||||
if a.GetAttributes() != nil {
|
||||
attrs := a.GetAttributes()
|
||||
|
||||
catalogMetricsArtifact.Name = attrs.Name
|
||||
catalogMetricsArtifact.ExternalId = attrs.ExternalID
|
||||
|
||||
if attrs.CreateTimeSinceEpoch != nil {
|
||||
createTime := strconv.FormatInt(*attrs.CreateTimeSinceEpoch, 10)
|
||||
catalogMetricsArtifact.CreateTimeSinceEpoch = &createTime
|
||||
}
|
||||
|
||||
if attrs.LastUpdateTimeSinceEpoch != nil {
|
||||
updateTime := strconv.FormatInt(*attrs.LastUpdateTimeSinceEpoch, 10)
|
||||
catalogMetricsArtifact.LastUpdateTimeSinceEpoch = &updateTime
|
||||
}
|
||||
}
|
||||
|
||||
if a.GetProperties() != nil {
|
||||
for _, prop := range *a.GetProperties() {
|
||||
switch prop.Name {
|
||||
case "description":
|
||||
if prop.StringValue != nil {
|
||||
catalogMetricsArtifact.Description = prop.StringValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map custom properties
|
||||
if a.GetCustomProperties() != nil && len(*a.GetCustomProperties()) > 0 {
|
||||
customPropsMap, err := converter.MapEmbedMDCustomProperties(*a.GetCustomProperties())
|
||||
if err != nil {
|
||||
return apimodels.CatalogArtifact{}, fmt.Errorf("error mapping custom properties: %w", err)
|
||||
}
|
||||
|
||||
catalogCustomProps := convertMetadataValueMap(customPropsMap)
|
||||
catalogMetricsArtifact.CustomProperties = &catalogCustomProps
|
||||
|
||||
}
|
||||
|
||||
return apimodels.CatalogArtifact{
|
||||
CatalogMetricsArtifact: catalogMetricsArtifact,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// convertMetadataValueMap converts from pkg/openapi.MetadataValue to catalog/pkg/openapi.MetadataValue
|
||||
func convertMetadataValueMap(source map[string]openapi.MetadataValue) map[string]apimodels.MetadataValue {
|
||||
result := make(map[string]apimodels.MetadataValue)
|
||||
|
||||
for key, value := range source {
|
||||
catalogValue := apimodels.MetadataValue{}
|
||||
|
||||
if value.MetadataStringValue != nil {
|
||||
catalogValue.MetadataStringValue = &apimodels.MetadataStringValue{
|
||||
StringValue: value.MetadataStringValue.StringValue,
|
||||
MetadataType: value.MetadataStringValue.MetadataType,
|
||||
}
|
||||
} else if value.MetadataIntValue != nil {
|
||||
catalogValue.MetadataIntValue = &apimodels.MetadataIntValue{
|
||||
IntValue: value.MetadataIntValue.IntValue,
|
||||
MetadataType: value.MetadataIntValue.MetadataType,
|
||||
}
|
||||
} else if value.MetadataDoubleValue != nil {
|
||||
catalogValue.MetadataDoubleValue = &apimodels.MetadataDoubleValue{
|
||||
DoubleValue: value.MetadataDoubleValue.DoubleValue,
|
||||
MetadataType: value.MetadataDoubleValue.MetadataType,
|
||||
}
|
||||
} else if value.MetadataBoolValue != nil {
|
||||
catalogValue.MetadataBoolValue = &apimodels.MetadataBoolValue{
|
||||
BoolValue: value.MetadataBoolValue.BoolValue,
|
||||
MetadataType: value.MetadataBoolValue.MetadataType,
|
||||
}
|
||||
} else if value.MetadataStructValue != nil {
|
||||
catalogValue.MetadataStructValue = &apimodels.MetadataStructValue{
|
||||
StructValue: value.MetadataStructValue.StructValue,
|
||||
MetadataType: value.MetadataStructValue.MetadataType,
|
||||
}
|
||||
}
|
||||
|
||||
result[key] = catalogValue
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -0,0 +1,704 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/service"
|
||||
model "github.com/kubeflow/model-registry/catalog/pkg/openapi"
|
||||
"github.com/kubeflow/model-registry/internal/apiutils"
|
||||
mr_models "github.com/kubeflow/model-registry/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/internal/db/schema"
|
||||
"github.com/kubeflow/model-registry/internal/testutils"
|
||||
"github.com/kubeflow/model-registry/pkg/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(testutils.TestMainHelper(m))
|
||||
}
|
||||
|
||||
func TestDBCatalog(t *testing.T) {
|
||||
// Setup test database
|
||||
sharedDB, cleanup := testutils.SetupMySQLWithMigrations(t, service.DatastoreSpec())
|
||||
defer cleanup()
|
||||
|
||||
// Get type IDs
|
||||
catalogModelTypeID := getCatalogModelTypeIDForDBTest(t, sharedDB)
|
||||
modelArtifactTypeID := getCatalogModelArtifactTypeIDForDBTest(t, sharedDB)
|
||||
metricsArtifactTypeID := getCatalogMetricsArtifactTypeIDForDBTest(t, sharedDB)
|
||||
|
||||
// Create repositories
|
||||
catalogModelRepo := service.NewCatalogModelRepository(sharedDB, catalogModelTypeID)
|
||||
catalogArtifactRepo := service.NewCatalogArtifactRepository(sharedDB, map[string]int64{
|
||||
service.CatalogModelArtifactTypeName: modelArtifactTypeID,
|
||||
service.CatalogMetricsArtifactTypeName: metricsArtifactTypeID,
|
||||
})
|
||||
modelArtifactRepo := service.NewCatalogModelArtifactRepository(sharedDB, modelArtifactTypeID)
|
||||
metricsArtifactRepo := service.NewCatalogMetricsArtifactRepository(sharedDB, metricsArtifactTypeID)
|
||||
|
||||
// Create DB catalog instance
|
||||
dbCatalog := NewDBCatalog(catalogModelRepo, catalogArtifactRepo)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("TestNewDBCatalog", func(t *testing.T) {
|
||||
catalog := NewDBCatalog(catalogModelRepo, catalogArtifactRepo)
|
||||
require.NotNil(t, catalog)
|
||||
|
||||
// Verify it implements the interface
|
||||
var _ CatalogSourceProvider = catalog
|
||||
})
|
||||
|
||||
t.Run("TestGetModel_Success", func(t *testing.T) {
|
||||
// Create test model
|
||||
testModel := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("test-get-model"),
|
||||
ExternalID: apiutils.Of("test-get-model-ext"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("test-source-id")},
|
||||
{Name: "description", StringValue: apiutils.Of("Test model description")},
|
||||
},
|
||||
}
|
||||
|
||||
savedModel, err := catalogModelRepo.Save(testModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test GetModel
|
||||
retrievedModel, err := dbCatalog.GetModel(ctx, "test-get-model", "test-source-id")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, retrievedModel)
|
||||
|
||||
assert.Equal(t, "test-get-model", retrievedModel.Name)
|
||||
assert.Equal(t, strconv.FormatInt(int64(*savedModel.GetID()), 10), *retrievedModel.Id)
|
||||
assert.Equal(t, "test-get-model-ext", *retrievedModel.ExternalId)
|
||||
assert.Equal(t, "test-source-id", *retrievedModel.SourceId)
|
||||
assert.Equal(t, "Test model description", *retrievedModel.Description)
|
||||
})
|
||||
|
||||
t.Run("TestGetModel_NotFound", func(t *testing.T) {
|
||||
// Test with non-existent model
|
||||
_, err := dbCatalog.GetModel(ctx, "non-existent-model", "test-source-id")
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "no models found")
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
})
|
||||
|
||||
t.Run("TestGetModel_DatabaseConstraints", func(t *testing.T) {
|
||||
// Test database constraint behavior - attempting to create duplicate models should fail
|
||||
// Using timestamp to ensure uniqueness across test runs
|
||||
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||
modelName := "constraint-test-model-" + timestamp
|
||||
sourceID := "constraint-test-source-" + timestamp
|
||||
|
||||
model1 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of(modelName),
|
||||
ExternalID: apiutils.Of("constraint-test-1-" + timestamp),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of(sourceID)},
|
||||
},
|
||||
}
|
||||
|
||||
// First model should save successfully
|
||||
savedModel1, err := catalogModelRepo.Save(model1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, savedModel1)
|
||||
|
||||
// Now test that GetModel works correctly with the single saved model
|
||||
retrievedModel, err := dbCatalog.GetModel(ctx, modelName, sourceID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, retrievedModel)
|
||||
assert.Equal(t, modelName, retrievedModel.Name)
|
||||
|
||||
// Test attempting to create a duplicate with same name but different external ID
|
||||
// This should fail due to database constraints (which is expected behavior)
|
||||
model2 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of(modelName), // Same name
|
||||
ExternalID: apiutils.Of("constraint-test-2-" + timestamp), // Different external ID
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of(sourceID)},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = catalogModelRepo.Save(model2)
|
||||
// This should fail due to database constraints preventing duplicate names
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "duplicated key")
|
||||
})
|
||||
|
||||
t.Run("TestListModels_Success", func(t *testing.T) {
|
||||
// Create test models
|
||||
sourceIDs := []string{"list-test-source"}
|
||||
|
||||
model1 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("list-test-model-1"),
|
||||
ExternalID: apiutils.Of("list-test-1"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("list-test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("First test model")},
|
||||
},
|
||||
}
|
||||
|
||||
model2 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("list-test-model-2"),
|
||||
ExternalID: apiutils.Of("list-test-2"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("list-test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("Second test model")},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := catalogModelRepo.Save(model1)
|
||||
require.NoError(t, err)
|
||||
_, err = catalogModelRepo.Save(model2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test ListModels
|
||||
params := ListModelsParams{
|
||||
SourceIDs: sourceIDs,
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
result, err := dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.GreaterOrEqual(t, len(result.Items), 2, "Should return at least 2 models")
|
||||
assert.Equal(t, int32(10), result.PageSize)
|
||||
assert.GreaterOrEqual(t, result.Size, int32(2))
|
||||
|
||||
// Verify models are properly mapped
|
||||
modelNames := make(map[string]bool)
|
||||
for _, model := range result.Items {
|
||||
modelNames[model.Name] = true
|
||||
// Verify required fields are present
|
||||
assert.NotEmpty(t, *model.Id)
|
||||
assert.NotEmpty(t, *model.SourceId)
|
||||
}
|
||||
|
||||
// Should contain our test models
|
||||
foundCount := 0
|
||||
if modelNames["list-test-model-1"] {
|
||||
foundCount++
|
||||
}
|
||||
if modelNames["list-test-model-2"] {
|
||||
foundCount++
|
||||
}
|
||||
assert.GreaterOrEqual(t, foundCount, 2, "Should find our test models")
|
||||
})
|
||||
|
||||
t.Run("TestListModels_WithPagination", func(t *testing.T) {
|
||||
// Test pagination
|
||||
sourceIDs := []string{"pagination-test-source"}
|
||||
|
||||
// Create multiple models
|
||||
for i := 0; i < 5; i++ {
|
||||
model := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of(fmt.Sprintf("pagination-test-model-%d", i)),
|
||||
ExternalID: apiutils.Of(fmt.Sprintf("pagination-test-%d", i)),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("pagination-test-source")},
|
||||
},
|
||||
}
|
||||
_, err := catalogModelRepo.Save(model)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
params := ListModelsParams{
|
||||
SourceIDs: sourceIDs,
|
||||
PageSize: 3,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
result, err := dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.LessOrEqual(t, len(result.Items), 3, "Should respect page size")
|
||||
assert.Equal(t, int32(3), result.PageSize)
|
||||
})
|
||||
|
||||
t.Run("TestListModels_WithQuery", func(t *testing.T) {
|
||||
// Create test models with different properties for query filtering
|
||||
sourceIDs := []string{"query-test-source"}
|
||||
|
||||
model1 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("BERT-base-model"),
|
||||
ExternalID: apiutils.Of("bert-base-1"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("query-test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("BERT base model for NLP tasks")},
|
||||
{Name: "provider", StringValue: apiutils.Of("Hugging Face")},
|
||||
{Name: "tasks", StringValue: apiutils.Of(`["text-classification", "question-answering"]`)},
|
||||
},
|
||||
}
|
||||
|
||||
model2 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("GPT-3.5-turbo"),
|
||||
ExternalID: apiutils.Of("gpt-35-turbo-1"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("query-test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("OpenAI GPT model for text generation")},
|
||||
{Name: "provider", StringValue: apiutils.Of("OpenAI")},
|
||||
{Name: "tasks", StringValue: apiutils.Of(`["text-generation", "conversational"]`)},
|
||||
},
|
||||
}
|
||||
|
||||
model3 := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("ResNet-50-image"),
|
||||
ExternalID: apiutils.Of("resnet-50-1"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("query-test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("Deep learning model for image classification")},
|
||||
{Name: "provider", StringValue: apiutils.Of("PyTorch")},
|
||||
{Name: "tasks", StringValue: apiutils.Of(`["image-classification", "computer-vision"]`)},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := catalogModelRepo.Save(model1)
|
||||
require.NoError(t, err)
|
||||
_, err = catalogModelRepo.Save(model2)
|
||||
require.NoError(t, err)
|
||||
_, err = catalogModelRepo.Save(model3)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test query filtering by name
|
||||
params := ListModelsParams{
|
||||
Query: "BERT",
|
||||
SourceIDs: sourceIDs,
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
result, err := dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model matching 'BERT'")
|
||||
assert.Contains(t, result.Items[0].Name, "BERT", "Should contain BERT model")
|
||||
|
||||
// Test query filtering by description
|
||||
params.Query = "NLP"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model with 'NLP' in description")
|
||||
assert.Contains(t, result.Items[0].Name, "BERT", "Should contain BERT model")
|
||||
|
||||
// Test query filtering by provider
|
||||
params.Query = "OpenAI"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model from 'OpenAI' provider")
|
||||
assert.Contains(t, result.Items[0].Name, "GPT", "Should contain GPT model")
|
||||
|
||||
// Test query filtering that should match multiple models
|
||||
params.Query = "model"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.GreaterOrEqual(t, result.Size, int32(3), "Should return at least 3 models matching 'model'")
|
||||
|
||||
// Test query that should return no results
|
||||
params.Query = "nonexistent"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(0), result.Size, "Should return 0 models for nonexistent query")
|
||||
|
||||
// Test query filtering by tasks - text-classification
|
||||
params.Query = "text-classification"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model with 'text-classification' task")
|
||||
assert.Contains(t, result.Items[0].Name, "BERT", "Should contain BERT model")
|
||||
|
||||
// Test query filtering by tasks - image-classification
|
||||
params.Query = "image-classification"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model with 'image-classification' task")
|
||||
assert.Contains(t, result.Items[0].Name, "ResNet", "Should contain ResNet model")
|
||||
|
||||
// Test query filtering by tasks - conversational
|
||||
params.Query = "conversational"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model with 'conversational' task")
|
||||
assert.Contains(t, result.Items[0].Name, "GPT", "Should contain GPT model")
|
||||
|
||||
// Test query filtering by tasks - partial match on "classification"
|
||||
params.Query = "classification"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(2), result.Size, "Should return 2 models with 'classification' in their tasks")
|
||||
|
||||
// Test query filtering by tasks - computer-vision
|
||||
params.Query = "computer-vision"
|
||||
result, err = dbCatalog.ListModels(ctx, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int32(1), result.Size, "Should return 1 model with 'computer-vision' task")
|
||||
assert.Contains(t, result.Items[0].Name, "ResNet", "Should contain ResNet model")
|
||||
})
|
||||
|
||||
t.Run("TestGetArtifacts_Success", func(t *testing.T) {
|
||||
// Create test model
|
||||
testModel := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("artifact-test-model"),
|
||||
ExternalID: apiutils.Of("artifact-test-model-ext"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("artifact-test-source")},
|
||||
},
|
||||
}
|
||||
|
||||
savedModel, err := catalogModelRepo.Save(testModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create test artifacts
|
||||
modelArtifact := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("test-model-artifact"),
|
||||
ExternalID: apiutils.Of("test-model-artifact-ext"),
|
||||
URI: apiutils.Of("s3://test/model.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
}
|
||||
|
||||
metricsArtifact := &models.CatalogMetricsArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("test-metrics-artifact"),
|
||||
ExternalID: apiutils.Of("test-metrics-artifact-ext"),
|
||||
MetricsType: models.MetricsTypeAccuracy,
|
||||
ArtifactType: apiutils.Of("metrics-artifact"),
|
||||
},
|
||||
}
|
||||
|
||||
savedModelArt, err := modelArtifactRepo.Save(modelArtifact, savedModel.GetID())
|
||||
require.NoError(t, err)
|
||||
savedMetricsArt, err := metricsArtifactRepo.Save(metricsArtifact, savedModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test GetArtifacts
|
||||
params := ListArtifactsParams{
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
result, err := dbCatalog.GetArtifacts(ctx, "artifact-test-model", "artifact-test-source", params)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.GreaterOrEqual(t, len(result.Items), 2, "Should return at least 2 artifacts")
|
||||
assert.Equal(t, int32(10), result.PageSize)
|
||||
|
||||
// Verify both types of artifacts are returned
|
||||
var modelArtifactFound, metricsArtifactFound bool
|
||||
artifactIDs := make(map[string]bool)
|
||||
|
||||
for _, artifact := range result.Items {
|
||||
if artifact.CatalogModelArtifact != nil {
|
||||
modelArtifactFound = true
|
||||
artifactIDs[*artifact.CatalogModelArtifact.Id] = true
|
||||
assert.Equal(t, "model-artifact", artifact.CatalogModelArtifact.ArtifactType)
|
||||
}
|
||||
if artifact.CatalogMetricsArtifact != nil {
|
||||
metricsArtifactFound = true
|
||||
artifactIDs[*artifact.CatalogMetricsArtifact.Id] = true
|
||||
assert.Equal(t, "metrics-artifact", artifact.CatalogMetricsArtifact.ArtifactType)
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, modelArtifactFound, "Should find model artifact")
|
||||
assert.True(t, metricsArtifactFound, "Should find metrics artifact")
|
||||
|
||||
// Verify our specific artifacts are in the results
|
||||
modelArtifactIDStr := strconv.FormatInt(int64(*savedModelArt.GetID()), 10)
|
||||
metricsArtifactIDStr := strconv.FormatInt(int64(*savedMetricsArt.GetID()), 10)
|
||||
assert.True(t, artifactIDs[modelArtifactIDStr], "Should contain our model artifact")
|
||||
assert.True(t, artifactIDs[metricsArtifactIDStr], "Should contain our metrics artifact")
|
||||
})
|
||||
|
||||
t.Run("TestGetArtifacts_ModelNotFound", func(t *testing.T) {
|
||||
// Test with non-existent model
|
||||
params := ListArtifactsParams{
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
_, err := dbCatalog.GetArtifacts(ctx, "non-existent-model", "test-source", params)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid model name")
|
||||
})
|
||||
|
||||
t.Run("TestGetArtifacts_WithCustomProperties", func(t *testing.T) {
|
||||
// Create model
|
||||
testModel := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("custom-props-model"),
|
||||
ExternalID: apiutils.Of("custom-props-model-ext"),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("custom-props-source")},
|
||||
},
|
||||
}
|
||||
|
||||
savedModel, err := catalogModelRepo.Save(testModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create artifact with custom properties
|
||||
customProps := []mr_models.Properties{
|
||||
{Name: "custom_prop_1", StringValue: apiutils.Of("value_1")},
|
||||
{Name: "custom_prop_2", StringValue: apiutils.Of("value_2")},
|
||||
}
|
||||
|
||||
artifactWithProps := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("artifact-with-props"),
|
||||
ExternalID: apiutils.Of("artifact-with-props-ext"),
|
||||
URI: apiutils.Of("s3://test/props.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
CustomProperties: &customProps,
|
||||
}
|
||||
|
||||
_, err = modelArtifactRepo.Save(artifactWithProps, savedModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get artifacts and verify custom properties
|
||||
params := ListArtifactsParams{
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
result, err := dbCatalog.GetArtifacts(ctx, "custom-props-model", "custom-props-source", params)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Find our artifact and check custom properties
|
||||
found := false
|
||||
for _, artifact := range result.Items {
|
||||
if artifact.CatalogModelArtifact != nil &&
|
||||
artifact.CatalogModelArtifact.Name != nil &&
|
||||
*artifact.CatalogModelArtifact.Name == "artifact-with-props" {
|
||||
|
||||
found = true
|
||||
assert.NotNil(t, artifact.CatalogModelArtifact.CustomProperties)
|
||||
|
||||
// Verify custom properties are present and properly converted
|
||||
customPropsMap := *artifact.CatalogModelArtifact.CustomProperties
|
||||
assert.Contains(t, customPropsMap, "custom_prop_1")
|
||||
assert.Contains(t, customPropsMap, "custom_prop_2")
|
||||
|
||||
// Verify the values are properly converted to MetadataValue
|
||||
prop1 := customPropsMap["custom_prop_1"]
|
||||
assert.NotNil(t, prop1.MetadataStringValue)
|
||||
assert.Equal(t, "value_1", prop1.MetadataStringValue.StringValue)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Should find artifact with custom properties")
|
||||
})
|
||||
|
||||
t.Run("TestMappingFunctions", func(t *testing.T) {
|
||||
t.Run("TestMapCatalogModelToCatalogModel", func(t *testing.T) {
|
||||
// Create a catalog model with various properties
|
||||
catalogModel := &models.CatalogModelImpl{
|
||||
ID: apiutils.Of(int32(123)),
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("mapping-test-model"),
|
||||
ExternalID: apiutils.Of("mapping-test-ext"),
|
||||
CreateTimeSinceEpoch: apiutils.Of(int64(1234567890)),
|
||||
LastUpdateTimeSinceEpoch: apiutils.Of(int64(1234567891)),
|
||||
},
|
||||
Properties: &[]mr_models.Properties{
|
||||
{Name: "source_id", StringValue: apiutils.Of("test-source")},
|
||||
{Name: "description", StringValue: apiutils.Of("Test description")},
|
||||
{Name: "library_name", StringValue: apiutils.Of("pytorch")},
|
||||
{Name: "language", StringValue: apiutils.Of("[\"python\", \"go\"]")},
|
||||
{Name: "tasks", StringValue: apiutils.Of("[\"classification\", \"regression\"]")},
|
||||
},
|
||||
}
|
||||
|
||||
result := mapDBModelToAPIModel(catalogModel)
|
||||
|
||||
assert.Equal(t, "123", *result.Id)
|
||||
assert.Equal(t, "mapping-test-model", result.Name)
|
||||
assert.Equal(t, "mapping-test-ext", *result.ExternalId)
|
||||
assert.Equal(t, "test-source", *result.SourceId)
|
||||
assert.Equal(t, "Test description", *result.Description)
|
||||
assert.Equal(t, "pytorch", *result.LibraryName)
|
||||
assert.Equal(t, "1234567890", *result.CreateTimeSinceEpoch)
|
||||
assert.Equal(t, "1234567891", *result.LastUpdateTimeSinceEpoch)
|
||||
|
||||
// Verify JSON arrays are properly parsed
|
||||
assert.Equal(t, []string{"python", "go"}, result.Language)
|
||||
assert.Equal(t, []string{"classification", "regression"}, result.Tasks)
|
||||
})
|
||||
|
||||
t.Run("TestMapCatalogArtifactToCatalogArtifact", func(t *testing.T) {
|
||||
// Test model artifact mapping
|
||||
var catalogModelArtifact models.CatalogModelArtifact = &models.CatalogModelArtifactImpl{
|
||||
ID: apiutils.Of(int32(456)),
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("test-model-artifact"),
|
||||
ExternalID: apiutils.Of("test-model-artifact-ext"),
|
||||
URI: apiutils.Of("s3://test/model.bin"),
|
||||
},
|
||||
}
|
||||
|
||||
catalogArtifact := models.CatalogArtifact{
|
||||
CatalogModelArtifact: &catalogModelArtifact,
|
||||
}
|
||||
|
||||
result, err := mapDBArtifactToAPIArtifact(catalogArtifact)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, result.CatalogModelArtifact)
|
||||
assert.Nil(t, result.CatalogMetricsArtifact)
|
||||
assert.Equal(t, "456", *result.CatalogModelArtifact.Id)
|
||||
assert.Equal(t, "test-model-artifact", *result.CatalogModelArtifact.Name)
|
||||
assert.Equal(t, "s3://test/model.bin", result.CatalogModelArtifact.Uri)
|
||||
|
||||
// Test metrics artifact mapping
|
||||
var catalogMetricsArtifact models.CatalogMetricsArtifact = &models.CatalogMetricsArtifactImpl{
|
||||
ID: apiutils.Of(int32(789)),
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("test-metrics-artifact"),
|
||||
ExternalID: apiutils.Of("test-metrics-artifact-ext"),
|
||||
MetricsType: models.MetricsTypePerformance,
|
||||
},
|
||||
}
|
||||
|
||||
catalogArtifact2 := models.CatalogArtifact{
|
||||
CatalogMetricsArtifact: &catalogMetricsArtifact,
|
||||
}
|
||||
|
||||
result2, err := mapDBArtifactToAPIArtifact(catalogArtifact2)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Nil(t, result2.CatalogModelArtifact)
|
||||
assert.NotNil(t, result2.CatalogMetricsArtifact)
|
||||
assert.Equal(t, "789", *result2.CatalogMetricsArtifact.Id)
|
||||
assert.Equal(t, "test-metrics-artifact", *result2.CatalogMetricsArtifact.Name)
|
||||
assert.Equal(t, "performance-metrics", result2.CatalogMetricsArtifact.MetricsType)
|
||||
})
|
||||
|
||||
t.Run("TestMapCatalogArtifact_EmptyArtifact", func(t *testing.T) {
|
||||
// Test with empty catalog artifact
|
||||
emptyCatalogArtifact := models.CatalogArtifact{}
|
||||
|
||||
_, err := mapDBArtifactToAPIArtifact(emptyCatalogArtifact)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid catalog artifact type")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("TestErrorHandling", func(t *testing.T) {
|
||||
t.Run("TestGetArtifacts_InvalidModelID", func(t *testing.T) {
|
||||
// Create a model with invalid ID format for testing
|
||||
// This would be an edge case where the ID isn't a valid integer
|
||||
|
||||
// We can't easily test this directly since IDs are generated as integers
|
||||
// But we can test the error case by mocking a scenario
|
||||
|
||||
// For now, let's test a scenario where the model exists but has some issue
|
||||
params := ListArtifactsParams{
|
||||
PageSize: 10,
|
||||
OrderBy: model.ORDERBYFIELD_CREATE_TIME,
|
||||
SortOrder: model.SORTORDER_ASC,
|
||||
NextPageToken: apiutils.Of(""),
|
||||
}
|
||||
|
||||
_, err := dbCatalog.GetArtifacts(ctx, "non-existent-model", "test-source", params)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid model name")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions to get type IDs from database
|
||||
|
||||
func getCatalogModelTypeIDForDBTest(t *testing.T, db *gorm.DB) int64 {
|
||||
var typeRecord schema.Type
|
||||
err := db.Where("name = ?", service.CatalogModelTypeName).First(&typeRecord).Error
|
||||
if err != nil {
|
||||
require.NoError(t, err, "Failed to query CatalogModel type")
|
||||
}
|
||||
return int64(typeRecord.ID)
|
||||
}
|
||||
|
||||
func getCatalogModelArtifactTypeIDForDBTest(t *testing.T, db *gorm.DB) int64 {
|
||||
var typeRecord schema.Type
|
||||
err := db.Where("name = ?", service.CatalogModelArtifactTypeName).First(&typeRecord).Error
|
||||
if err != nil {
|
||||
require.NoError(t, err, "Failed to query CatalogModelArtifact type")
|
||||
}
|
||||
return int64(typeRecord.ID)
|
||||
}
|
||||
|
||||
func getCatalogMetricsArtifactTypeIDForDBTest(t *testing.T, db *gorm.DB) int64 {
|
||||
var typeRecord schema.Type
|
||||
err := db.Where("name = ?", service.CatalogMetricsArtifactTypeName).First(&typeRecord).Error
|
||||
if err != nil {
|
||||
require.NoError(t, err, "Failed to query CatalogMetricsArtifact type")
|
||||
}
|
||||
return int64(typeRecord.ID)
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ const (
|
|||
defaultHuggingFaceURL = "https://huggingface.co"
|
||||
)
|
||||
|
||||
func (h *hfCatalogImpl) GetModel(ctx context.Context, name string) (*openapi.CatalogModel, error) {
|
||||
func (h *hfCatalogImpl) GetModel(ctx context.Context, modelName string, sourceID string) (*openapi.CatalogModel, error) {
|
||||
// TODO: Implement HuggingFace model retrieval
|
||||
return nil, fmt.Errorf("HuggingFace model retrieval not yet implemented")
|
||||
}
|
||||
|
|
@ -39,10 +39,10 @@ func (h *hfCatalogImpl) ListModels(ctx context.Context, params ListModelsParams)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (h *hfCatalogImpl) GetArtifacts(ctx context.Context, name string) (*openapi.CatalogArtifactList, error) {
|
||||
func (h *hfCatalogImpl) GetArtifacts(ctx context.Context, modelName string, sourceID string, params ListArtifactsParams) (openapi.CatalogArtifactList, error) {
|
||||
// TODO: Implement HuggingFace model artifacts retrieval
|
||||
// For now, return empty list to satisfy interface
|
||||
return &openapi.CatalogArtifactList{
|
||||
return openapi.CatalogArtifactList{
|
||||
Items: []openapi.CatalogArtifact{},
|
||||
PageSize: 0,
|
||||
Size: 0,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func TestNewHfCatalog_WithValidCredentials(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
// Test GetModel - should return not implemented error
|
||||
model, err := hfCatalog.GetModel(ctx, "test-model")
|
||||
model, err := hfCatalog.GetModel(ctx, "test-model", "")
|
||||
if err == nil {
|
||||
t.Fatal("Expected not implemented error, got nil")
|
||||
}
|
||||
|
|
@ -99,11 +99,11 @@ func TestNewHfCatalog_WithValidCredentials(t *testing.T) {
|
|||
}
|
||||
|
||||
// Test GetArtifacts - should return empty list
|
||||
artifacts, err := hfCatalog.GetArtifacts(ctx, "test-model")
|
||||
artifacts, err := hfCatalog.GetArtifacts(ctx, "test-model", "", ListArtifactsParams{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get artifacts: %v", err)
|
||||
}
|
||||
if artifacts == nil {
|
||||
if artifacts.Items == nil {
|
||||
t.Fatal("Expected artifacts list, got nil")
|
||||
}
|
||||
if len(artifacts.Items) != 0 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
[mysqld]
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_general_ci
|
||||
|
||||
!includedir /etc/mysql/conf.d/
|
||||
|
|
@ -68,11 +68,11 @@ type yamlCatalogImpl struct {
|
|||
|
||||
var _ CatalogSourceProvider = &yamlCatalogImpl{}
|
||||
|
||||
func (y *yamlCatalogImpl) GetModel(ctx context.Context, name string) (*model.CatalogModel, error) {
|
||||
func (y *yamlCatalogImpl) GetModel(ctx context.Context, modelName string, sourceID string) (*model.CatalogModel, error) {
|
||||
y.modelsLock.RLock()
|
||||
defer y.modelsLock.RUnlock()
|
||||
|
||||
ym := y.models[name]
|
||||
ym := y.models[modelName]
|
||||
if ym == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -159,13 +159,13 @@ func (y *yamlCatalogImpl) ListModels(ctx context.Context, params ListModelsParam
|
|||
return list, nil // Return the struct value directly
|
||||
}
|
||||
|
||||
func (y *yamlCatalogImpl) GetArtifacts(ctx context.Context, name string) (*model.CatalogArtifactList, error) {
|
||||
func (y *yamlCatalogImpl) GetArtifacts(ctx context.Context, modelName string, sourceID string, params ListArtifactsParams) (model.CatalogArtifactList, error) {
|
||||
y.modelsLock.RLock()
|
||||
defer y.modelsLock.RUnlock()
|
||||
|
||||
ym := y.models[name]
|
||||
ym := y.models[modelName]
|
||||
if ym == nil {
|
||||
return nil, nil
|
||||
return model.CatalogArtifactList{}, nil
|
||||
}
|
||||
|
||||
count := len(ym.Artifacts)
|
||||
|
|
@ -181,7 +181,7 @@ func (y *yamlCatalogImpl) GetArtifacts(ctx context.Context, name string) (*model
|
|||
for i := range list.Items {
|
||||
list.Items[i] = ym.Artifacts[i].CatalogArtifact
|
||||
}
|
||||
return &list, nil
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func isModelExcluded(modelName string, patterns []string) bool {
|
||||
|
|
|
|||
|
|
@ -12,20 +12,20 @@ func TestYAMLCatalogGetModel(t *testing.T) {
|
|||
assert := assert.New(t)
|
||||
provider := testYAMLProvider(t, "testdata/test-yaml-catalog.yaml")
|
||||
|
||||
model, err := provider.GetModel(context.Background(), "rhelai1/granite-8b-code-base")
|
||||
model, err := provider.GetModel(context.Background(), "rhelai1/granite-8b-code-base", "")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal("rhelai1/granite-8b-code-base", model.Name)
|
||||
|
||||
newLogo := "foobar"
|
||||
model.Logo = &newLogo
|
||||
|
||||
model2, err := provider.GetModel(context.Background(), "rhelai1/granite-8b-code-base")
|
||||
model2, err := provider.GetModel(context.Background(), "rhelai1/granite-8b-code-base", "")
|
||||
if assert.NoError(err) {
|
||||
assert.NotEqual(model2.Logo, model.Logo, "changes to one returned object should not affect other return values")
|
||||
}
|
||||
}
|
||||
|
||||
notFound, err := provider.GetModel(context.Background(), "foo")
|
||||
notFound, err := provider.GetModel(context.Background(), "foo", "")
|
||||
assert.NoError(err)
|
||||
assert.Nil(notFound)
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ func TestYAMLCatalogGetArtifacts(t *testing.T) {
|
|||
provider := testYAMLProvider(t, "testdata/test-yaml-catalog.yaml")
|
||||
|
||||
// Test case 1: Model with artifacts
|
||||
artifacts, err := provider.GetArtifacts(context.Background(), "rhelai1/granite-8b-code-base")
|
||||
artifacts, err := provider.GetArtifacts(context.Background(), "rhelai1/granite-8b-code-base", "", ListArtifactsParams{})
|
||||
if assert.NoError(err) {
|
||||
assert.NotNil(artifacts)
|
||||
assert.Equal(int32(2), artifacts.Size)
|
||||
|
|
@ -52,7 +52,7 @@ func TestYAMLCatalogGetArtifacts(t *testing.T) {
|
|||
}
|
||||
|
||||
// Test case 2: Model with no artifacts
|
||||
noArtifactsModel, err := provider.GetArtifacts(context.Background(), "model-with-no-artifacts")
|
||||
noArtifactsModel, err := provider.GetArtifacts(context.Background(), "model-with-no-artifacts", "", ListArtifactsParams{})
|
||||
if assert.NoError(err) {
|
||||
assert.NotNil(noArtifactsModel)
|
||||
assert.Equal(int32(0), noArtifactsModel.Size)
|
||||
|
|
@ -61,9 +61,9 @@ func TestYAMLCatalogGetArtifacts(t *testing.T) {
|
|||
}
|
||||
|
||||
// Test case 3: Model not found
|
||||
notFoundArtifacts, err := provider.GetArtifacts(context.Background(), "non-existent-model")
|
||||
notFoundArtifacts, err := provider.GetArtifacts(context.Background(), "non-existent-model", "", ListArtifactsParams{})
|
||||
assert.NoError(err)
|
||||
assert.Nil(notFoundArtifacts)
|
||||
assert.Equal(int32(0), notFoundArtifacts.Size)
|
||||
}
|
||||
|
||||
func TestYAMLCatalogListModels(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/kubeflow/model-registry/internal/db/filter"
|
||||
"github.com/kubeflow/model-registry/internal/db/models"
|
||||
)
|
||||
|
||||
type CatalogArtifactListOptions struct {
|
||||
models.Pagination
|
||||
Name *string
|
||||
ExternalID *string
|
||||
ParentResourceID *int32
|
||||
ArtifactType *string
|
||||
}
|
||||
|
||||
// GetRestEntityType implements the FilterApplier interface
|
||||
// This enables advanced filtering support for catalog artifacts
|
||||
func (c *CatalogArtifactListOptions) GetRestEntityType() filter.RestEntityType {
|
||||
// Determine the appropriate REST entity type based on artifact type
|
||||
if c.ArtifactType != nil {
|
||||
switch *c.ArtifactType {
|
||||
case "model-artifact":
|
||||
return filter.RestEntityModelArtifact
|
||||
case "metrics-artifact":
|
||||
return filter.RestEntityModelArtifact // Reusing existing filter type
|
||||
}
|
||||
}
|
||||
// Default to ModelArtifact if no specific type is provided
|
||||
return filter.RestEntityModelArtifact
|
||||
}
|
||||
|
||||
// CatalogArtifact is a discriminated union that can hold different catalog artifact types
|
||||
type CatalogArtifact struct {
|
||||
CatalogModelArtifact *CatalogModelArtifact
|
||||
CatalogMetricsArtifact *CatalogMetricsArtifact
|
||||
}
|
||||
|
||||
type CatalogArtifactRepository interface {
|
||||
GetByID(id int32) (CatalogArtifact, error)
|
||||
List(listOptions CatalogArtifactListOptions) (*models.ListWrapper[CatalogArtifact], error)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ type MetricsType string
|
|||
const (
|
||||
MetricsTypePerformance MetricsType = "performance-metrics"
|
||||
MetricsTypeAccuracy MetricsType = "accuracy-metrics"
|
||||
CatalogMetricsArtifactType = "metrics-artifact"
|
||||
)
|
||||
|
||||
type CatalogMetricsArtifactListOptions struct {
|
||||
|
|
@ -26,6 +27,7 @@ func (c *CatalogMetricsArtifactListOptions) GetRestEntityType() filter.RestEntit
|
|||
|
||||
type CatalogMetricsArtifactAttributes struct {
|
||||
Name *string
|
||||
ArtifactType *string
|
||||
MetricsType MetricsType
|
||||
ExternalID *string
|
||||
CreateTimeSinceEpoch *int64
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ type CatalogModelListOptions struct {
|
|||
models.Pagination
|
||||
Name *string
|
||||
ExternalID *string
|
||||
SourceIDs *[]string
|
||||
Query *string
|
||||
}
|
||||
|
||||
// GetRestEntityType implements the FilterApplier interface
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"github.com/kubeflow/model-registry/internal/db/models"
|
||||
)
|
||||
|
||||
const CatalogModelArtifactType = "catalog-model-artifact"
|
||||
const CatalogModelArtifactType = "model-artifact"
|
||||
|
||||
type CatalogModelArtifactListOptions struct {
|
||||
models.Pagination
|
||||
|
|
@ -22,6 +22,7 @@ func (c *CatalogModelArtifactListOptions) GetRestEntityType() filter.RestEntityT
|
|||
type CatalogModelArtifactAttributes struct {
|
||||
Name *string
|
||||
URI *string
|
||||
ArtifactType *string
|
||||
ExternalID *string
|
||||
CreateTimeSinceEpoch *int64
|
||||
LastUpdateTimeSinceEpoch *int64
|
||||
|
|
|
|||
|
|
@ -0,0 +1,217 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/internal/datastore"
|
||||
dbmodels "github.com/kubeflow/model-registry/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/internal/db/schema"
|
||||
"github.com/kubeflow/model-registry/internal/db/scopes"
|
||||
"github.com/kubeflow/model-registry/internal/db/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var ErrCatalogArtifactNotFound = errors.New("catalog artifact by id not found")
|
||||
|
||||
type CatalogArtifactRepositoryImpl struct {
|
||||
db *gorm.DB
|
||||
idToName map[int64]string
|
||||
nameToID datastore.ArtifactTypeMap
|
||||
}
|
||||
|
||||
func NewCatalogArtifactRepository(db *gorm.DB, artifactTypes datastore.ArtifactTypeMap) models.CatalogArtifactRepository {
|
||||
idToName := make(map[int64]string, len(artifactTypes))
|
||||
for name, id := range artifactTypes {
|
||||
idToName[id] = name
|
||||
}
|
||||
|
||||
return &CatalogArtifactRepositoryImpl{
|
||||
db: db,
|
||||
nameToID: artifactTypes,
|
||||
idToName: idToName,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *CatalogArtifactRepositoryImpl) GetByID(id int32) (models.CatalogArtifact, error) {
|
||||
artifact := &schema.Artifact{}
|
||||
properties := []schema.ArtifactProperty{}
|
||||
|
||||
if err := r.db.Where("id = ?", id).First(artifact).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return models.CatalogArtifact{}, fmt.Errorf("%w: %v", ErrCatalogArtifactNotFound, err)
|
||||
}
|
||||
return models.CatalogArtifact{}, fmt.Errorf("error getting catalog artifact by id: %w", err)
|
||||
}
|
||||
|
||||
if err := r.db.Where("artifact_id = ?", artifact.ID).Find(&properties).Error; err != nil {
|
||||
return models.CatalogArtifact{}, fmt.Errorf("error getting properties by artifact id: %w", err)
|
||||
}
|
||||
|
||||
// Use the same logic as mapDataLayerToCatalogArtifact to handle artifact types
|
||||
mappedArtifact, err := r.mapDataLayerToCatalogArtifact(*artifact, properties)
|
||||
if err != nil {
|
||||
return models.CatalogArtifact{}, fmt.Errorf("error mapping catalog artifact: %w", err)
|
||||
}
|
||||
|
||||
return mappedArtifact, nil
|
||||
}
|
||||
|
||||
func (r *CatalogArtifactRepositoryImpl) List(listOptions models.CatalogArtifactListOptions) (*dbmodels.ListWrapper[models.CatalogArtifact], error) {
|
||||
list := dbmodels.ListWrapper[models.CatalogArtifact]{
|
||||
PageSize: listOptions.GetPageSize(),
|
||||
}
|
||||
|
||||
artifacts := []models.CatalogArtifact{}
|
||||
artifactsArt := []schema.Artifact{}
|
||||
|
||||
query := r.db.Model(&schema.Artifact{})
|
||||
|
||||
// Apply filters similar to the internal artifact service
|
||||
if listOptions.Name != nil {
|
||||
// Name is not prefixed with the parent resource id to allow for filtering by name only
|
||||
// Parent resource Id is used later to filter by Attribution.context_id
|
||||
query = query.Where("name LIKE ?", fmt.Sprintf("%%:%s", *listOptions.Name))
|
||||
} else if listOptions.ExternalID != nil {
|
||||
query = query.Where("external_id = ?", listOptions.ExternalID)
|
||||
}
|
||||
|
||||
// Filter by artifact type if specified
|
||||
if listOptions.ArtifactType != nil {
|
||||
typeID, err := r.getTypeIDFromArtifactType(*listOptions.ArtifactType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid catalog artifact type %s: %w", *listOptions.ArtifactType, err)
|
||||
}
|
||||
query = query.Where("type_id = ?", typeID)
|
||||
} else {
|
||||
// Only include catalog artifact types
|
||||
catalogTypeIDs := []int64{}
|
||||
for _, typeID := range r.nameToID {
|
||||
catalogTypeIDs = append(catalogTypeIDs, typeID)
|
||||
}
|
||||
query = query.Where("type_id IN ?", catalogTypeIDs)
|
||||
}
|
||||
|
||||
// Apply parent resource filtering if specified
|
||||
if listOptions.ParentResourceID != nil {
|
||||
// Proper GORM JOIN: Use helper that respects naming strategy
|
||||
query = query.Joins(utils.BuildAttributionJoin(query)).
|
||||
Where(utils.GetColumnRef(query, &schema.Attribution{}, "context_id")+" = ?", listOptions.ParentResourceID).
|
||||
Select(utils.GetTableName(query, &schema.Artifact{}) + ".*") // Explicitly select from Artifact table to avoid ambiguity
|
||||
}
|
||||
|
||||
orderBy := listOptions.GetOrderBy()
|
||||
sortOrder := listOptions.GetSortOrder()
|
||||
nextPageToken := listOptions.GetNextPageToken()
|
||||
pageSize := listOptions.GetPageSize()
|
||||
|
||||
pagination := &dbmodels.Pagination{
|
||||
PageSize: &pageSize,
|
||||
OrderBy: &orderBy,
|
||||
SortOrder: &sortOrder,
|
||||
NextPageToken: &nextPageToken,
|
||||
}
|
||||
|
||||
query = query.Scopes(scopes.PaginateWithTablePrefix(artifactsArt, pagination, r.db, "Artifact"))
|
||||
|
||||
if err := query.Find(&artifactsArt).Error; err != nil {
|
||||
return nil, fmt.Errorf("error listing catalog artifacts: %w", err)
|
||||
}
|
||||
|
||||
hasMore := false
|
||||
if pageSize > 0 {
|
||||
hasMore = len(artifactsArt) > int(pageSize)
|
||||
if hasMore {
|
||||
artifactsArt = artifactsArt[:len(artifactsArt)-1] // Remove the extra item used for hasMore detection
|
||||
}
|
||||
}
|
||||
|
||||
// Map each artifact with its properties
|
||||
for _, artifactArt := range artifactsArt {
|
||||
properties := []schema.ArtifactProperty{}
|
||||
if err := r.db.Where("artifact_id = ?", artifactArt.ID).Find(&properties).Error; err != nil {
|
||||
return nil, fmt.Errorf("error getting properties by artifact id: %w", err)
|
||||
}
|
||||
|
||||
artifact, err := r.mapDataLayerToCatalogArtifact(artifactArt, properties)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error mapping catalog artifact: %w", err)
|
||||
}
|
||||
artifacts = append(artifacts, artifact)
|
||||
}
|
||||
|
||||
// Handle pagination token - generate token when there are more pages
|
||||
if hasMore && len(artifactsArt) > 0 {
|
||||
// Use the last artifact to generate pagination token
|
||||
lastArtifact := artifactsArt[len(artifactsArt)-1]
|
||||
nextToken := r.createPaginationToken(lastArtifact, listOptions)
|
||||
listOptions.NextPageToken = &nextToken
|
||||
} else {
|
||||
listOptions.NextPageToken = nil
|
||||
}
|
||||
|
||||
list.Items = artifacts
|
||||
list.NextPageToken = listOptions.GetNextPageToken()
|
||||
list.Size = int32(len(artifacts))
|
||||
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
// getTypeIDFromArtifactType maps catalog artifact type strings to their corresponding type IDs
|
||||
func (r *CatalogArtifactRepositoryImpl) getTypeIDFromArtifactType(artifactType string) (int64, error) {
|
||||
switch artifactType {
|
||||
case "model-artifact":
|
||||
return r.nameToID[CatalogModelArtifactTypeName], nil
|
||||
case "metrics-artifact":
|
||||
return r.nameToID[CatalogMetricsArtifactTypeName], nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported catalog artifact type: %s", artifactType)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *CatalogArtifactRepositoryImpl) mapDataLayerToCatalogArtifact(artifact schema.Artifact, properties []schema.ArtifactProperty) (models.CatalogArtifact, error) {
|
||||
artToReturn := models.CatalogArtifact{}
|
||||
|
||||
typeName := r.idToName[int64(artifact.TypeID)]
|
||||
|
||||
switch typeName {
|
||||
case CatalogModelArtifactTypeName:
|
||||
modelArtifact := mapDataLayerToCatalogModelArtifact(artifact, properties)
|
||||
artToReturn.CatalogModelArtifact = &modelArtifact
|
||||
case CatalogMetricsArtifactTypeName:
|
||||
metricsArtifact := mapDataLayerToCatalogMetricsArtifact(artifact, properties)
|
||||
artToReturn.CatalogMetricsArtifact = &metricsArtifact
|
||||
default:
|
||||
return models.CatalogArtifact{}, fmt.Errorf("invalid catalog artifact type: %s=%d (expected: %v)", typeName, artifact.TypeID, r.idToName)
|
||||
}
|
||||
|
||||
return artToReturn, nil
|
||||
}
|
||||
|
||||
// createPaginationToken generates a pagination token based on the last artifact and ordering
|
||||
func (r *CatalogArtifactRepositoryImpl) createPaginationToken(artifact schema.Artifact, listOptions models.CatalogArtifactListOptions) string {
|
||||
orderBy := listOptions.GetOrderBy()
|
||||
value := ""
|
||||
|
||||
// Generate token value based on ordering field
|
||||
switch orderBy {
|
||||
case "ID":
|
||||
value = fmt.Sprintf("%d", artifact.ID)
|
||||
case "CREATE_TIME":
|
||||
value = fmt.Sprintf("%d", artifact.CreateTimeSinceEpoch)
|
||||
case "LAST_UPDATE_TIME":
|
||||
value = fmt.Sprintf("%d", artifact.LastUpdateTimeSinceEpoch)
|
||||
case "NAME":
|
||||
if artifact.Name != nil {
|
||||
value = *artifact.Name
|
||||
} else {
|
||||
value = fmt.Sprintf("%d", artifact.ID) // Fallback to ID if name is nil
|
||||
}
|
||||
default:
|
||||
// Default to ID ordering
|
||||
value = fmt.Sprintf("%d", artifact.ID)
|
||||
}
|
||||
|
||||
return scopes.CreateNextPageToken(artifact.ID, value)
|
||||
}
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
package service_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/service"
|
||||
"github.com/kubeflow/model-registry/internal/apiutils"
|
||||
dbmodels "github.com/kubeflow/model-registry/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/internal/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCatalogArtifactRepository(t *testing.T) {
|
||||
sharedDB, cleanup := testutils.SetupMySQLWithMigrations(t, service.DatastoreSpec())
|
||||
defer cleanup()
|
||||
|
||||
// Get the catalog artifact type IDs
|
||||
modelArtifactTypeID := getCatalogModelArtifactTypeID(t, sharedDB)
|
||||
metricsArtifactTypeID := getCatalogMetricsArtifactTypeID(t, sharedDB)
|
||||
|
||||
// Create unified artifact repository with both types
|
||||
artifactTypeMap := map[string]int64{
|
||||
service.CatalogModelArtifactTypeName: modelArtifactTypeID,
|
||||
service.CatalogMetricsArtifactTypeName: metricsArtifactTypeID,
|
||||
}
|
||||
repo := service.NewCatalogArtifactRepository(sharedDB, artifactTypeMap)
|
||||
|
||||
// Also get CatalogModel type ID for creating parent entities
|
||||
catalogModelTypeID := getCatalogModelTypeID(t, sharedDB)
|
||||
catalogModelRepo := service.NewCatalogModelRepository(sharedDB, catalogModelTypeID)
|
||||
modelArtifactRepo := service.NewCatalogModelArtifactRepository(sharedDB, modelArtifactTypeID)
|
||||
metricsArtifactRepo := service.NewCatalogMetricsArtifactRepository(sharedDB, metricsArtifactTypeID)
|
||||
|
||||
// Create shared test data
|
||||
catalogModel := &models.CatalogModelImpl{
|
||||
TypeID: apiutils.Of(int32(catalogModelTypeID)),
|
||||
Attributes: &models.CatalogModelAttributes{
|
||||
Name: apiutils.Of("test-catalog-model-for-artifacts"),
|
||||
ExternalID: apiutils.Of("catalog-model-artifacts-ext-123"),
|
||||
},
|
||||
}
|
||||
savedCatalogModel, err := catalogModelRepo.Save(catalogModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("GetByID_ModelArtifact", func(t *testing.T) {
|
||||
// Create a model artifact using the specific repository
|
||||
modelArtifact := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("test-model-artifact-getbyid"),
|
||||
ExternalID: apiutils.Of("model-art-getbyid-ext-123"),
|
||||
URI: apiutils.Of("s3://test-bucket/model.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
}
|
||||
savedModelArtifact, err := modelArtifactRepo.Save(modelArtifact, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve using unified repository
|
||||
retrieved, err := repo.GetByID(*savedModelArtifact.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's a model artifact
|
||||
assert.NotNil(t, retrieved.CatalogModelArtifact)
|
||||
assert.Nil(t, retrieved.CatalogMetricsArtifact)
|
||||
assert.Equal(t, "test-model-artifact-getbyid", *(*retrieved.CatalogModelArtifact).GetAttributes().Name)
|
||||
assert.Equal(t, "model-art-getbyid-ext-123", *(*retrieved.CatalogModelArtifact).GetAttributes().ExternalID)
|
||||
assert.Equal(t, "s3://test-bucket/model.bin", *(*retrieved.CatalogModelArtifact).GetAttributes().URI)
|
||||
})
|
||||
|
||||
t.Run("GetByID_MetricsArtifact", func(t *testing.T) {
|
||||
// Create a metrics artifact using the specific repository
|
||||
metricsArtifact := &models.CatalogMetricsArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("test-metrics-artifact-getbyid"),
|
||||
ExternalID: apiutils.Of("metrics-art-getbyid-ext-123"),
|
||||
MetricsType: models.MetricsTypeAccuracy,
|
||||
ArtifactType: apiutils.Of("metrics-artifact"),
|
||||
},
|
||||
}
|
||||
savedMetricsArtifact, err := metricsArtifactRepo.Save(metricsArtifact, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve using unified repository
|
||||
retrieved, err := repo.GetByID(*savedMetricsArtifact.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's a metrics artifact
|
||||
assert.Nil(t, retrieved.CatalogModelArtifact)
|
||||
assert.NotNil(t, retrieved.CatalogMetricsArtifact)
|
||||
assert.Equal(t, "test-metrics-artifact-getbyid", *(*retrieved.CatalogMetricsArtifact).GetAttributes().Name)
|
||||
assert.Equal(t, "metrics-art-getbyid-ext-123", *(*retrieved.CatalogMetricsArtifact).GetAttributes().ExternalID)
|
||||
assert.Equal(t, models.MetricsTypeAccuracy, (*retrieved.CatalogMetricsArtifact).GetAttributes().MetricsType)
|
||||
})
|
||||
|
||||
t.Run("GetByID_NotFound", func(t *testing.T) {
|
||||
nonExistentID := int32(99999)
|
||||
_, err := repo.GetByID(nonExistentID)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "catalog artifact by id not found")
|
||||
})
|
||||
|
||||
t.Run("List_AllArtifacts", func(t *testing.T) {
|
||||
// Create test artifacts of both types
|
||||
modelArtifact1 := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("test-model-artifact-list-1"),
|
||||
ExternalID: apiutils.Of("model-list-1-ext"),
|
||||
URI: apiutils.Of("s3://test/model1.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
}
|
||||
|
||||
modelArtifact2 := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("test-model-artifact-list-2"),
|
||||
ExternalID: apiutils.Of("model-list-2-ext"),
|
||||
URI: apiutils.Of("s3://test/model2.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
}
|
||||
|
||||
metricsArtifact1 := &models.CatalogMetricsArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("test-metrics-artifact-list-1"),
|
||||
ExternalID: apiutils.Of("metrics-list-1-ext"),
|
||||
MetricsType: models.MetricsTypeAccuracy,
|
||||
ArtifactType: apiutils.Of("metrics-artifact"),
|
||||
},
|
||||
}
|
||||
|
||||
// Save artifacts
|
||||
savedModelArt1, err := modelArtifactRepo.Save(modelArtifact1, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
savedModelArt2, err := modelArtifactRepo.Save(modelArtifact2, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
savedMetricsArt1, err := metricsArtifactRepo.Save(metricsArtifact1, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// List all artifacts for the parent resource
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ParentResourceID: savedCatalogModel.GetID(),
|
||||
}
|
||||
|
||||
result, err := repo.List(listOptions)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
// Should return all 3 artifacts (2 model + 1 metrics)
|
||||
assert.GreaterOrEqual(t, len(result.Items), 3, "Should return at least the 3 artifacts we created")
|
||||
|
||||
// Verify we got both types
|
||||
var modelArtifactCount, metricsArtifactCount int
|
||||
artifactIDs := make(map[int32]bool)
|
||||
|
||||
for _, artifact := range result.Items {
|
||||
if artifact.CatalogModelArtifact != nil {
|
||||
modelArtifactCount++
|
||||
artifactIDs[*(*artifact.CatalogModelArtifact).GetID()] = true
|
||||
} else if artifact.CatalogMetricsArtifact != nil {
|
||||
metricsArtifactCount++
|
||||
artifactIDs[*(*artifact.CatalogMetricsArtifact).GetID()] = true
|
||||
}
|
||||
}
|
||||
|
||||
assert.GreaterOrEqual(t, modelArtifactCount, 2, "Should have at least 2 model artifacts")
|
||||
assert.GreaterOrEqual(t, metricsArtifactCount, 1, "Should have at least 1 metrics artifact")
|
||||
|
||||
// Verify our specific artifacts are in the results
|
||||
assert.True(t, artifactIDs[*savedModelArt1.GetID()], "Should contain first model artifact")
|
||||
assert.True(t, artifactIDs[*savedModelArt2.GetID()], "Should contain second model artifact")
|
||||
assert.True(t, artifactIDs[*savedMetricsArt1.GetID()], "Should contain metrics artifact")
|
||||
})
|
||||
|
||||
t.Run("List_FilterByArtifactType_ModelArtifact", func(t *testing.T) {
|
||||
// Filter by model artifact type only
|
||||
artifactType := "model-artifact"
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ParentResourceID: savedCatalogModel.GetID(),
|
||||
ArtifactType: &artifactType,
|
||||
}
|
||||
|
||||
result, err := repo.List(listOptions)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
// All results should be model artifacts
|
||||
for _, artifact := range result.Items {
|
||||
assert.NotNil(t, artifact.CatalogModelArtifact, "Should only return model artifacts")
|
||||
assert.Nil(t, artifact.CatalogMetricsArtifact, "Should not return metrics artifacts")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("List_FilterByArtifactType_MetricsArtifact", func(t *testing.T) {
|
||||
// Filter by metrics artifact type only
|
||||
artifactType := "metrics-artifact"
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ParentResourceID: savedCatalogModel.GetID(),
|
||||
ArtifactType: &artifactType,
|
||||
}
|
||||
|
||||
result, err := repo.List(listOptions)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
// All results should be metrics artifacts
|
||||
for _, artifact := range result.Items {
|
||||
assert.Nil(t, artifact.CatalogModelArtifact, "Should not return model artifacts")
|
||||
assert.NotNil(t, artifact.CatalogMetricsArtifact, "Should only return metrics artifacts")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("List_FilterByExternalID", func(t *testing.T) {
|
||||
// Create artifact with specific external ID for filtering
|
||||
testArtifact := &models.CatalogMetricsArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("external-id-filter-test"),
|
||||
ExternalID: apiutils.Of("unique-external-id-123"),
|
||||
MetricsType: models.MetricsTypePerformance,
|
||||
ArtifactType: apiutils.Of("metrics-artifact"),
|
||||
},
|
||||
}
|
||||
savedArtifact, err := metricsArtifactRepo.Save(testArtifact, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Filter by external ID
|
||||
externalID := "unique-external-id-123"
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ExternalID: &externalID,
|
||||
}
|
||||
|
||||
result, err := repo.List(listOptions)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
assert.Len(t, result.Items, 1, "Should find exactly one artifact with the external ID")
|
||||
|
||||
// Verify it's the correct artifact
|
||||
artifact := result.Items[0]
|
||||
assert.NotNil(t, artifact.CatalogMetricsArtifact)
|
||||
assert.Equal(t, *savedArtifact.GetID(), *(*artifact.CatalogMetricsArtifact).GetID())
|
||||
assert.Equal(t, "unique-external-id-123", *(*artifact.CatalogMetricsArtifact).GetAttributes().ExternalID)
|
||||
})
|
||||
|
||||
t.Run("List_WithPagination", func(t *testing.T) {
|
||||
// Create multiple artifacts for pagination testing
|
||||
for i := 0; i < 5; i++ {
|
||||
artifact := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of(fmt.Sprintf("pagination-test-%d", i)),
|
||||
ExternalID: apiutils.Of(fmt.Sprintf("pagination-ext-%d", i)),
|
||||
URI: apiutils.Of(fmt.Sprintf("s3://test/pagination-%d.bin", i)),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
}
|
||||
_, err := modelArtifactRepo.Save(artifact, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Test pagination
|
||||
pageSize := int32(3)
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ParentResourceID: savedCatalogModel.GetID(),
|
||||
Pagination: dbmodels.Pagination{
|
||||
PageSize: &pageSize,
|
||||
OrderBy: apiutils.Of("ID"),
|
||||
},
|
||||
}
|
||||
|
||||
result, err := repo.List(listOptions)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
assert.LessOrEqual(t, len(result.Items), 3, "Should respect page size limit")
|
||||
assert.GreaterOrEqual(t, len(result.Items), 1, "Should return at least one item")
|
||||
})
|
||||
|
||||
t.Run("List_InvalidArtifactType", func(t *testing.T) {
|
||||
// Test with invalid artifact type
|
||||
invalidType := "invalid-artifact-type"
|
||||
listOptions := models.CatalogArtifactListOptions{
|
||||
ParentResourceID: savedCatalogModel.GetID(),
|
||||
ArtifactType: &invalidType,
|
||||
}
|
||||
|
||||
_, err := repo.List(listOptions)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid catalog artifact type")
|
||||
assert.Contains(t, err.Error(), "invalid-artifact-type")
|
||||
})
|
||||
|
||||
t.Run("List_WithCustomProperties", func(t *testing.T) {
|
||||
// Create artifacts with custom properties
|
||||
customProps := []dbmodels.Properties{
|
||||
{
|
||||
Name: "custom_prop_1",
|
||||
StringValue: apiutils.Of("custom_value_1"),
|
||||
},
|
||||
{
|
||||
Name: "custom_prop_2",
|
||||
StringValue: apiutils.Of("custom_value_2"),
|
||||
},
|
||||
}
|
||||
|
||||
artifactWithCustomProps := &models.CatalogModelArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(modelArtifactTypeID)),
|
||||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: apiutils.Of("artifact-with-custom-props"),
|
||||
ExternalID: apiutils.Of("custom-props-ext"),
|
||||
URI: apiutils.Of("s3://test/custom-props.bin"),
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
},
|
||||
CustomProperties: &customProps,
|
||||
}
|
||||
|
||||
savedArtifact, err := modelArtifactRepo.Save(artifactWithCustomProps, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve using unified repository
|
||||
retrieved, err := repo.GetByID(*savedArtifact.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify custom properties are preserved
|
||||
assert.NotNil(t, retrieved.CatalogModelArtifact)
|
||||
assert.NotNil(t, (*retrieved.CatalogModelArtifact).GetCustomProperties())
|
||||
|
||||
customPropsMap := make(map[string]string)
|
||||
for _, prop := range *(*retrieved.CatalogModelArtifact).GetCustomProperties() {
|
||||
if prop.StringValue != nil {
|
||||
customPropsMap[prop.Name] = *prop.StringValue
|
||||
}
|
||||
}
|
||||
|
||||
assert.Equal(t, "custom_value_1", customPropsMap["custom_prop_1"])
|
||||
assert.Equal(t, "custom_value_2", customPropsMap["custom_prop_2"])
|
||||
})
|
||||
|
||||
t.Run("MappingErrors", func(t *testing.T) {
|
||||
// Test error handling for invalid type mapping
|
||||
// This would typically happen if there's data inconsistency in the database
|
||||
|
||||
// We can't easily test this without directly manipulating the database
|
||||
// but we can test the GetByID with an artifact that has an unknown type
|
||||
// by temporarily modifying the repository's type mapping
|
||||
|
||||
// Create a repository with incomplete type mapping
|
||||
incompleteTypeMap := map[string]int64{
|
||||
service.CatalogModelArtifactTypeName: modelArtifactTypeID,
|
||||
// Missing CatalogMetricsArtifactTypeName intentionally
|
||||
}
|
||||
incompleteRepo := service.NewCatalogArtifactRepository(sharedDB, incompleteTypeMap)
|
||||
|
||||
// Create a metrics artifact first using the complete repo
|
||||
metricsArtifact := &models.CatalogMetricsArtifactImpl{
|
||||
TypeID: apiutils.Of(int32(metricsArtifactTypeID)),
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: apiutils.Of("test-mapping-error"),
|
||||
ExternalID: apiutils.Of("mapping-error-ext"),
|
||||
MetricsType: models.MetricsTypeAccuracy,
|
||||
ArtifactType: apiutils.Of("metrics-artifact"),
|
||||
},
|
||||
}
|
||||
savedMetricsArtifact, err := metricsArtifactRepo.Save(metricsArtifact, savedCatalogModel.GetID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to retrieve using incomplete repo - should get mapping error
|
||||
_, err = incompleteRepo.GetByID(*savedMetricsArtifact.GetID())
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid catalog artifact type")
|
||||
})
|
||||
}
|
||||
|
|
@ -131,6 +131,7 @@ func mapDataLayerToCatalogMetricsArtifact(artifact schema.Artifact, artPropertie
|
|||
TypeID: &artifact.TypeID,
|
||||
Attributes: &models.CatalogMetricsArtifactAttributes{
|
||||
Name: artifact.Name,
|
||||
ArtifactType: apiutils.Of(models.CatalogMetricsArtifactType),
|
||||
ExternalID: artifact.ExternalID,
|
||||
CreateTimeSinceEpoch: &artifact.CreateTimeSinceEpoch,
|
||||
LastUpdateTimeSinceEpoch: &artifact.LastUpdateTimeSinceEpoch,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ package service
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubeflow/model-registry/catalog/internal/db/models"
|
||||
dbmodels "github.com/kubeflow/model-registry/internal/db/models"
|
||||
"github.com/kubeflow/model-registry/internal/db/schema"
|
||||
"github.com/kubeflow/model-registry/internal/db/service"
|
||||
"github.com/kubeflow/model-registry/internal/db/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
|
@ -45,11 +48,54 @@ func (r *CatalogModelRepositoryImpl) List(listOptions models.CatalogModelListOpt
|
|||
}
|
||||
|
||||
func applyCatalogModelListFilters(query *gorm.DB, listOptions *models.CatalogModelListOptions) *gorm.DB {
|
||||
contextTable := utils.GetTableName(query.Statement.DB, &schema.Context{})
|
||||
|
||||
if listOptions.Name != nil {
|
||||
query = query.Where("name LIKE ?", listOptions.Name)
|
||||
query = query.Where(fmt.Sprintf("%s.name LIKE ?", contextTable), listOptions.Name)
|
||||
} else if listOptions.ExternalID != nil {
|
||||
query = query.Where("external_id = ?", listOptions.ExternalID)
|
||||
query = query.Where(fmt.Sprintf("%s.external_id = ?", contextTable), listOptions.ExternalID)
|
||||
}
|
||||
|
||||
if listOptions.Query != nil && *listOptions.Query != "" {
|
||||
queryPattern := fmt.Sprintf("%%%s%%", strings.ToLower(*listOptions.Query))
|
||||
propertyTable := utils.GetTableName(query.Statement.DB, &schema.ContextProperty{})
|
||||
|
||||
// Search in name (context table)
|
||||
nameCondition := fmt.Sprintf("LOWER(%s.name) LIKE ?", contextTable)
|
||||
|
||||
// Search in description, provider, libraryName properties
|
||||
propertyCondition := fmt.Sprintf("EXISTS (SELECT 1 FROM %s cp WHERE cp.context_id = %s.id AND cp.name IN (?, ?, ?) AND LOWER(cp.string_value) LIKE ?)",
|
||||
propertyTable, contextTable)
|
||||
|
||||
// Search in tasks (assuming tasks are stored as comma-separated or multiple properties)
|
||||
tasksCondition := fmt.Sprintf("EXISTS (SELECT 1 FROM %s cp WHERE cp.context_id = %s.id AND cp.name = ? AND LOWER(cp.string_value) LIKE ?)",
|
||||
propertyTable, contextTable)
|
||||
|
||||
query = query.Where(fmt.Sprintf("(%s OR %s OR %s)", nameCondition, propertyCondition, tasksCondition),
|
||||
queryPattern, // for name
|
||||
"description", "provider", "libraryName", queryPattern, // for properties
|
||||
"tasks", queryPattern, // for tasks
|
||||
)
|
||||
}
|
||||
|
||||
// Filter out empty strings from SourceIDs, for some reason it's passed if no sources are specified
|
||||
var nonEmptySourceIDs []string
|
||||
if listOptions.SourceIDs != nil {
|
||||
for _, sourceID := range *listOptions.SourceIDs {
|
||||
if sourceID != "" {
|
||||
nonEmptySourceIDs = append(nonEmptySourceIDs, sourceID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(nonEmptySourceIDs) > 0 {
|
||||
propertyTable := utils.GetTableName(query.Statement.DB, &schema.ContextProperty{})
|
||||
|
||||
joinClause := fmt.Sprintf("JOIN %s cp ON cp.context_id = %s.id", propertyTable, contextTable)
|
||||
query = query.Joins(joinClause).
|
||||
Where("cp.name = ? AND cp.string_value IN ?", "source_id", nonEmptySourceIDs)
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ func mapDataLayerToCatalogModelArtifact(artifact schema.Artifact, artProperties
|
|||
Attributes: &models.CatalogModelArtifactAttributes{
|
||||
Name: artifact.Name,
|
||||
URI: artifact.URI,
|
||||
ArtifactType: apiutils.Of(models.CatalogModelArtifactType),
|
||||
ExternalID: artifact.ExternalID,
|
||||
CreateTimeSinceEpoch: &artifact.CreateTimeSinceEpoch,
|
||||
LastUpdateTimeSinceEpoch: &artifact.LastUpdateTimeSinceEpoch,
|
||||
|
|
|
|||
|
|
@ -32,5 +32,6 @@ func DatastoreSpec() *datastore.Spec {
|
|||
).
|
||||
AddArtifact(CatalogMetricsArtifactTypeName, datastore.NewSpecType(NewCatalogMetricsArtifactRepository).
|
||||
AddString("metricsType"),
|
||||
)
|
||||
).
|
||||
AddOther(NewCatalogArtifactRepository)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ impl.go
|
|||
logger.go
|
||||
model_artifact_type_query_param.go
|
||||
model_base_model.go
|
||||
model_base_resource.go
|
||||
model_base_resource_dates.go
|
||||
model_base_resource_list.go
|
||||
model_catalog_artifact.go
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ type ModelCatalogServiceAPIRouter interface {
|
|||
// while the service implementation can be ignored with the .openapi-generator-ignore file
|
||||
// and updated with the logic required for the API.
|
||||
type ModelCatalogServiceAPIServicer interface {
|
||||
FindModels(context.Context, string, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
FindModels(context.Context, []string, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
FindSources(context.Context, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetModel(context.Context, string, string) (ImplResponse, error)
|
||||
GetAllModelArtifacts(context.Context, string, string) (ImplResponse, error)
|
||||
GetAllModelArtifacts(context.Context, string, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func (c *ModelCatalogServiceAPIController) Routes() Routes {
|
|||
// FindModels - Search catalog models across sources.
|
||||
func (c *ModelCatalogServiceAPIController) FindModels(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
sourceParam := query.Get("source")
|
||||
sourceParam := strings.Split(query.Get("source"), ",")
|
||||
qParam := query.Get("q")
|
||||
pageSizeParam := query.Get("pageSize")
|
||||
orderByParam := query.Get("orderBy")
|
||||
|
|
@ -128,9 +128,14 @@ func (c *ModelCatalogServiceAPIController) GetModel(w http.ResponseWriter, r *ht
|
|||
|
||||
// GetAllModelArtifacts - List CatalogArtifacts.
|
||||
func (c *ModelCatalogServiceAPIController) GetAllModelArtifacts(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
sourceIdParam := chi.URLParam(r, "source_id")
|
||||
modelNameParam := chi.URLParam(r, "model_name")
|
||||
result, err := c.service.GetAllModelArtifacts(r.Context(), sourceIdParam, modelNameParam)
|
||||
pageSizeParam := query.Get("pageSize")
|
||||
orderByParam := query.Get("orderBy")
|
||||
sortOrderParam := query.Get("sortOrder")
|
||||
nextPageTokenParam := query.Get("nextPageToken")
|
||||
result, err := c.service.GetAllModelArtifacts(r.Context(), sourceIdParam, modelNameParam, pageSizeParam, model.OrderByField(orderByParam), model.SortOrder(sortOrderParam), nextPageTokenParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, err, &result)
|
||||
|
|
|
|||
|
|
@ -8,87 +8,111 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kubeflow/model-registry/catalog/internal/catalog"
|
||||
model "github.com/kubeflow/model-registry/catalog/pkg/openapi"
|
||||
"github.com/kubeflow/model-registry/pkg/api"
|
||||
)
|
||||
|
||||
// ModelCatalogServiceAPIService is a service that implements the logic for the ModelCatalogServiceAPIServicer
|
||||
// This service should implement the business logic for every endpoint for the ModelCatalogServiceAPI s.coreApi.
|
||||
// Include any external packages or services that will be required by this service.
|
||||
type ModelCatalogServiceAPIService struct {
|
||||
provider catalog.CatalogSourceProvider
|
||||
sources *catalog.SourceCollection
|
||||
}
|
||||
|
||||
// GetAllModelArtifacts retrieves all model artifacts for a given model from the specified source.
|
||||
func (m *ModelCatalogServiceAPIService) GetAllModelArtifacts(ctx context.Context, sourceID string, name string) (ImplResponse, error) {
|
||||
source, ok := m.sources.Get(sourceID)
|
||||
if !ok {
|
||||
return notFound("Unknown source"), nil
|
||||
func (m *ModelCatalogServiceAPIService) GetAllModelArtifacts(ctx context.Context, sourceID string, modelName string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
if newName, err := url.PathUnescape(modelName); err == nil {
|
||||
modelName = newName
|
||||
}
|
||||
|
||||
if newName, err := url.PathUnescape(name); err == nil {
|
||||
name = newName
|
||||
}
|
||||
var err error
|
||||
pageSizeInt := int32(10)
|
||||
|
||||
artifacts, err := source.Provider.GetArtifacts(ctx, name)
|
||||
if pageSize != "" {
|
||||
parsed, err := strconv.ParseInt(pageSize, 10, 32)
|
||||
if err != nil {
|
||||
return Response(http.StatusInternalServerError, err), err
|
||||
return Response(http.StatusBadRequest, err), err
|
||||
}
|
||||
pageSizeInt = int32(parsed)
|
||||
}
|
||||
|
||||
artifacts, err := m.provider.GetArtifacts(ctx, modelName, sourceID, catalog.ListArtifactsParams{
|
||||
PageSize: pageSizeInt,
|
||||
OrderBy: orderBy,
|
||||
SortOrder: sortOrder,
|
||||
NextPageToken: &nextPageToken,
|
||||
})
|
||||
if err != nil {
|
||||
statusCode := api.ErrToStatus(err)
|
||||
var errorMsg string
|
||||
if errors.Is(err, api.ErrBadRequest) {
|
||||
errorMsg = fmt.Sprintf("Invalid model name '%s' for source '%s'", modelName, sourceID)
|
||||
} else if errors.Is(err, api.ErrNotFound) {
|
||||
errorMsg = fmt.Sprintf("No model found '%s' in source '%s'", modelName, sourceID)
|
||||
} else {
|
||||
errorMsg = err.Error()
|
||||
}
|
||||
return ErrorResponse(statusCode, errors.New(errorMsg)), err
|
||||
}
|
||||
|
||||
return Response(http.StatusOK, artifacts), nil
|
||||
}
|
||||
|
||||
func (m *ModelCatalogServiceAPIService) FindModels(ctx context.Context, sourceID string, q string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
source, ok := m.sources.Get(sourceID)
|
||||
if !ok {
|
||||
return notFound("Unknown source"), errors.New("Unknown source")
|
||||
}
|
||||
func (m *ModelCatalogServiceAPIService) FindModels(ctx context.Context, sourceIDs []string, q string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
var err error
|
||||
pageSizeInt := int32(10)
|
||||
|
||||
p, err := newPaginator[model.CatalogModel](pageSize, orderBy, sortOrder, nextPageToken)
|
||||
if pageSize != "" {
|
||||
parsed, err := strconv.ParseInt(pageSize, 10, 32)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusBadRequest, err), err
|
||||
return Response(http.StatusBadRequest, err), err
|
||||
}
|
||||
pageSizeInt = int32(parsed)
|
||||
}
|
||||
|
||||
listModelsParams := catalog.ListModelsParams{
|
||||
Query: q,
|
||||
OrderBy: p.OrderBy,
|
||||
SortOrder: p.SortOrder,
|
||||
SourceIDs: sourceIDs,
|
||||
PageSize: pageSizeInt,
|
||||
OrderBy: orderBy,
|
||||
SortOrder: sortOrder,
|
||||
NextPageToken: &nextPageToken,
|
||||
}
|
||||
|
||||
models, err := source.Provider.ListModels(ctx, listModelsParams)
|
||||
models, err := m.provider.ListModels(ctx, listModelsParams)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, err), err
|
||||
}
|
||||
|
||||
page, next := p.Paginate(models.Items)
|
||||
|
||||
models.Items = page
|
||||
models.PageSize = p.PageSize
|
||||
models.NextPageToken = next.Token()
|
||||
|
||||
return Response(http.StatusOK, models), nil
|
||||
}
|
||||
|
||||
func (m *ModelCatalogServiceAPIService) GetModel(ctx context.Context, sourceID string, name string) (ImplResponse, error) {
|
||||
if name, ok := strings.CutSuffix(name, "/artifacts"); ok {
|
||||
return m.GetAllModelArtifacts(ctx, sourceID, name)
|
||||
func (m *ModelCatalogServiceAPIService) GetModel(ctx context.Context, sourceID string, modelName string) (ImplResponse, error) {
|
||||
if name, ok := strings.CutSuffix(modelName, "/artifacts"); ok {
|
||||
return m.GetAllModelArtifacts(ctx, sourceID, name, "10", model.OrderByField(model.ORDERBYFIELD_CREATE_TIME), model.SortOrder(model.SORTORDER_ASC), "")
|
||||
}
|
||||
|
||||
source, ok := m.sources.Get(sourceID)
|
||||
if !ok {
|
||||
return notFound("Unknown source"), nil
|
||||
if newName, err := url.PathUnescape(modelName); err == nil {
|
||||
modelName = newName
|
||||
}
|
||||
|
||||
if newName, err := url.PathUnescape(name); err == nil {
|
||||
name = newName
|
||||
}
|
||||
|
||||
model, err := source.Provider.GetModel(ctx, name)
|
||||
model, err := m.provider.GetModel(ctx, modelName, sourceID)
|
||||
if err != nil {
|
||||
return Response(http.StatusInternalServerError, err), err
|
||||
statusCode := api.ErrToStatus(err)
|
||||
var errorMsg string
|
||||
if errors.Is(err, api.ErrNotFound) {
|
||||
errorMsg = fmt.Sprintf("No model found '%s' in source '%s'", modelName, sourceID)
|
||||
} else {
|
||||
errorMsg = err.Error()
|
||||
}
|
||||
return ErrorResponse(statusCode, errors.New(errorMsg)), err
|
||||
}
|
||||
|
||||
if model == nil {
|
||||
return notFound("Unknown model or version"), nil
|
||||
}
|
||||
|
|
@ -167,8 +191,9 @@ func genCatalogCmpFunc(orderBy model.OrderByField, sortOrder model.SortOrder) (f
|
|||
var _ ModelCatalogServiceAPIServicer = &ModelCatalogServiceAPIService{}
|
||||
|
||||
// NewModelCatalogServiceAPIService creates a default api service
|
||||
func NewModelCatalogServiceAPIService(sources *catalog.SourceCollection) ModelCatalogServiceAPIServicer {
|
||||
func NewModelCatalogServiceAPIService(provider catalog.CatalogSourceProvider, sources *catalog.SourceCollection) ModelCatalogServiceAPIServicer {
|
||||
return &ModelCatalogServiceAPIService{
|
||||
provider: provider,
|
||||
sources: sources,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,8 +188,13 @@ func TestFindModels(t *testing.T) {
|
|||
pageSize: "10",
|
||||
orderBy: model.ORDERBYFIELD_ID,
|
||||
sortOrder: model.SORTORDER_ASC,
|
||||
expectedStatus: http.StatusNotFound,
|
||||
expectedModelList: nil,
|
||||
expectedStatus: http.StatusOK, // Changed from http.StatusNotFound to http.StatusOK with an empty list -- now the source ID is just a field in the CatalogModel
|
||||
expectedModelList: &model.CatalogModelList{
|
||||
Items: []model.CatalogModel{},
|
||||
Size: 0,
|
||||
PageSize: 10,
|
||||
NextPageToken: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid pageSize string",
|
||||
|
|
@ -214,8 +219,15 @@ func TestFindModels(t *testing.T) {
|
|||
pageSize: "10",
|
||||
orderBy: "UNSUPPORTED_FIELD",
|
||||
sortOrder: model.SORTORDER_ASC,
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedModelList: nil,
|
||||
expectedStatus: http.StatusOK, // Changed from http.StatusBadRequest to http.StatusOK -- in model registry we fallback to ID if the order by field is unsupported
|
||||
expectedModelList: &model.CatalogModelList{
|
||||
Items: []model.CatalogModel{
|
||||
*modelA,
|
||||
},
|
||||
Size: 1,
|
||||
PageSize: 10,
|
||||
NextPageToken: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Unsupported sortOrder field",
|
||||
|
|
@ -227,8 +239,15 @@ func TestFindModels(t *testing.T) {
|
|||
pageSize: "10",
|
||||
orderBy: model.ORDERBYFIELD_ID,
|
||||
sortOrder: "UNSUPPORTED_ORDER",
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
expectedModelList: nil,
|
||||
expectedStatus: http.StatusOK, // Changed from http.StatusBadRequest to http.StatusOK -- in model registry we fallback to ASC if the sort order field is unsupported
|
||||
expectedModelList: &model.CatalogModelList{
|
||||
Items: []model.CatalogModel{
|
||||
*modelA,
|
||||
},
|
||||
Size: 1,
|
||||
PageSize: 10,
|
||||
NextPageToken: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty models in source",
|
||||
|
|
@ -277,11 +296,16 @@ func TestFindModels(t *testing.T) {
|
|||
},
|
||||
},
|
||||
})
|
||||
service := NewModelCatalogServiceAPIService(sources)
|
||||
|
||||
provider := &mockModelProvider{
|
||||
models: tc.mockModels,
|
||||
}
|
||||
|
||||
service := NewModelCatalogServiceAPIService(provider, sources)
|
||||
|
||||
resp, err := service.FindModels(
|
||||
context.Background(),
|
||||
tc.sourceID,
|
||||
[]string{tc.sourceID},
|
||||
tc.q,
|
||||
tc.pageSize,
|
||||
tc.orderBy,
|
||||
|
|
@ -635,7 +659,7 @@ func TestFindSources(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create service with test catalogs
|
||||
service := NewModelCatalogServiceAPIService(catalog.NewSourceCollection(tc.catalogs))
|
||||
service := NewModelCatalogServiceAPIService(&mockModelProvider{}, catalog.NewSourceCollection(tc.catalogs))
|
||||
|
||||
// Call FindSources
|
||||
resp, err := service.FindSources(
|
||||
|
|
@ -729,7 +753,7 @@ type mockModelProvider struct {
|
|||
}
|
||||
|
||||
// Implement GetModel method for the mock provider
|
||||
func (m *mockModelProvider) GetModel(ctx context.Context, name string) (*model.CatalogModel, error) {
|
||||
func (m *mockModelProvider) GetModel(ctx context.Context, name string, sourceID string) (*model.CatalogModel, error) {
|
||||
model, exists := m.models[name]
|
||||
if !exists {
|
||||
return nil, nil
|
||||
|
|
@ -771,30 +795,49 @@ func (m *mockModelProvider) ListModels(ctx context.Context, params catalog.ListM
|
|||
return cmp < 0
|
||||
})
|
||||
|
||||
items := make([]model.CatalogModel, len(filteredModels))
|
||||
for i, mdl := range filteredModels {
|
||||
totalSize := int32(len(filteredModels))
|
||||
pageSize := params.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// Apply pagination - limit items to page size
|
||||
endIndex := int(pageSize)
|
||||
if endIndex > len(filteredModels) {
|
||||
endIndex = len(filteredModels)
|
||||
}
|
||||
|
||||
pagedModels := filteredModels[:endIndex]
|
||||
items := make([]model.CatalogModel, len(pagedModels))
|
||||
for i, mdl := range pagedModels {
|
||||
items[i] = *mdl
|
||||
}
|
||||
|
||||
nextPageToken := ""
|
||||
if len(filteredModels) > int(pageSize) {
|
||||
lastItem := pagedModels[len(pagedModels)-1]
|
||||
nextPageToken = (&stringCursor{Value: lastItem.Name, ID: lastItem.Name}).String()
|
||||
}
|
||||
|
||||
return model.CatalogModelList{
|
||||
Items: items,
|
||||
Size: int32(len(items)),
|
||||
PageSize: int32(len(items)), // Mock returns all filtered items as one "page"
|
||||
NextPageToken: "",
|
||||
Size: totalSize,
|
||||
PageSize: pageSize,
|
||||
NextPageToken: nextPageToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockModelProvider) GetArtifacts(ctx context.Context, name string) (*model.CatalogArtifactList, error) {
|
||||
func (m *mockModelProvider) GetArtifacts(ctx context.Context, name string, sourceID string, params catalog.ListArtifactsParams) (model.CatalogArtifactList, error) {
|
||||
artifacts, exists := m.artifacts[name]
|
||||
if !exists {
|
||||
return &model.CatalogArtifactList{
|
||||
return model.CatalogArtifactList{
|
||||
Items: []model.CatalogArtifact{},
|
||||
Size: 0,
|
||||
PageSize: 0, // Or a default page size if applicable
|
||||
NextPageToken: "",
|
||||
}, nil
|
||||
}
|
||||
return &model.CatalogArtifactList{
|
||||
return model.CatalogArtifactList{
|
||||
Items: artifacts,
|
||||
Size: int32(len(artifacts)),
|
||||
PageSize: int32(len(artifacts)),
|
||||
|
|
@ -810,6 +853,7 @@ func TestGetModel(t *testing.T) {
|
|||
modelName string
|
||||
expectedStatus int
|
||||
expectedModel *model.CatalogModel
|
||||
provider catalog.CatalogSourceProvider
|
||||
}{
|
||||
{
|
||||
name: "Existing model in source",
|
||||
|
|
@ -825,6 +869,13 @@ func TestGetModel(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
models: map[string]*model.CatalogModel{
|
||||
"test-model": {
|
||||
Name: "test-model",
|
||||
},
|
||||
},
|
||||
},
|
||||
sourceID: "source1",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusOK,
|
||||
|
|
@ -839,6 +890,9 @@ func TestGetModel(t *testing.T) {
|
|||
Metadata: model.CatalogSource{Id: "source1", Name: "Test Source"},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
models: map[string]*model.CatalogModel{},
|
||||
},
|
||||
sourceID: "source2",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
|
|
@ -854,6 +908,9 @@ func TestGetModel(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
models: map[string]*model.CatalogModel{},
|
||||
},
|
||||
sourceID: "source1",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
|
|
@ -873,6 +930,13 @@ func TestGetModel(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
models: map[string]*model.CatalogModel{
|
||||
"some/model:v1.0.0": {
|
||||
Name: "some/model:v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
sourceID: "source1",
|
||||
modelName: "some%2Fmodel%3Av1.0.0",
|
||||
expectedStatus: http.StatusOK,
|
||||
|
|
@ -885,7 +949,7 @@ func TestGetModel(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create service with test sources
|
||||
service := NewModelCatalogServiceAPIService(catalog.NewSourceCollection(tc.sources))
|
||||
service := NewModelCatalogServiceAPIService(tc.provider, catalog.NewSourceCollection(tc.sources))
|
||||
|
||||
// Call GetModel
|
||||
resp, _ := service.GetModel(
|
||||
|
|
@ -923,6 +987,7 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
modelName string
|
||||
expectedStatus int
|
||||
expectedArtifacts []model.CatalogArtifact
|
||||
provider catalog.CatalogSourceProvider
|
||||
}{
|
||||
{
|
||||
name: "Existing artifacts for model in source",
|
||||
|
|
@ -947,6 +1012,22 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
artifacts: map[string][]model.CatalogArtifact{
|
||||
"test-model": {
|
||||
{
|
||||
CatalogModelArtifact: &model.CatalogModelArtifact{
|
||||
Uri: "s3://bucket/artifact1",
|
||||
},
|
||||
},
|
||||
{
|
||||
CatalogModelArtifact: &model.CatalogModelArtifact{
|
||||
Uri: "s3://bucket/artifact2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sourceID: "source1",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusOK,
|
||||
|
|
@ -970,10 +1051,13 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
Metadata: model.CatalogSource{Id: "source1", Name: "Test Source"},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
artifacts: map[string][]model.CatalogArtifact{},
|
||||
},
|
||||
sourceID: "source2",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
expectedArtifacts: nil,
|
||||
expectedStatus: http.StatusOK, // Changed from http.StatusNotFound to http.StatusOK -- having the same behavior as the model registry
|
||||
expectedArtifacts: []model.CatalogArtifact{},
|
||||
},
|
||||
{
|
||||
name: "Existing source, no artifacts for model",
|
||||
|
|
@ -985,6 +1069,9 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
provider: &mockModelProvider{
|
||||
artifacts: map[string][]model.CatalogArtifact{},
|
||||
},
|
||||
sourceID: "source1",
|
||||
modelName: "test-model",
|
||||
expectedStatus: http.StatusOK,
|
||||
|
|
@ -995,13 +1082,17 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create service with test sources
|
||||
service := NewModelCatalogServiceAPIService(catalog.NewSourceCollection(tc.sources))
|
||||
service := NewModelCatalogServiceAPIService(tc.provider, catalog.NewSourceCollection(tc.sources))
|
||||
|
||||
// Call GetAllModelArtifacts
|
||||
resp, _ := service.GetAllModelArtifacts(
|
||||
context.Background(),
|
||||
tc.sourceID,
|
||||
tc.modelName,
|
||||
"10",
|
||||
model.ORDERBYFIELD_CREATE_TIME,
|
||||
model.SORTORDER_ASC,
|
||||
"",
|
||||
)
|
||||
|
||||
// Check response status
|
||||
|
|
@ -1016,7 +1107,7 @@ func TestGetAllModelArtifacts(t *testing.T) {
|
|||
require.NotNil(t, resp.Body)
|
||||
|
||||
// Type assertion to access the list of artifacts
|
||||
artifactList, ok := resp.Body.(*model.CatalogArtifactList)
|
||||
artifactList, ok := resp.Body.(model.CatalogArtifactList)
|
||||
require.True(t, ok, "Response body should be a CatalogArtifactList")
|
||||
|
||||
// Check the artifacts
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ func AssertBaseModelRequired(obj model.BaseModel) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AssertBaseResourceConstraints checks if the values respects the defined constraints
|
||||
func AssertBaseResourceConstraints(obj model.BaseResource) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertBaseResourceDatesConstraints checks if the values respects the defined constraints
|
||||
func AssertBaseResourceDatesConstraints(obj model.BaseResourceDates) error {
|
||||
return nil
|
||||
|
|
@ -67,6 +72,11 @@ func AssertBaseResourceListRequired(obj model.BaseResourceList) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AssertBaseResourceRequired checks if the required fields are not zero-ed
|
||||
func AssertBaseResourceRequired(obj model.BaseResource) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssertCatalogArtifactListConstraints checks if the values respects the defined constraints
|
||||
func AssertCatalogArtifactListConstraints(obj model.CatalogArtifactList) error {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ client.go
|
|||
configuration.go
|
||||
model_artifact_type_query_param.go
|
||||
model_base_model.go
|
||||
model_base_resource.go
|
||||
model_base_resource_dates.go
|
||||
model_base_resource_list.go
|
||||
model_catalog_artifact.go
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ type ModelCatalogServiceAPIService service
|
|||
type ApiFindModelsRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ModelCatalogServiceAPIService
|
||||
source *string
|
||||
source *[]string
|
||||
q *string
|
||||
pageSize *string
|
||||
orderBy *OrderByField
|
||||
|
|
@ -33,8 +34,8 @@ type ApiFindModelsRequest struct {
|
|||
nextPageToken *string
|
||||
}
|
||||
|
||||
// Filter models by source. This parameter is currently required and may only be specified once.
|
||||
func (r ApiFindModelsRequest) Source(source string) ApiFindModelsRequest {
|
||||
// Filter models by source. This parameter can be specified multiple times to filter by multiple sources (OR logic). For example: ?source=huggingface&source=local will return models from either huggingface OR local sources.
|
||||
func (r ApiFindModelsRequest) Source(source []string) ApiFindModelsRequest {
|
||||
r.source = &source
|
||||
return r
|
||||
}
|
||||
|
|
@ -107,11 +108,18 @@ func (a *ModelCatalogServiceAPIService) FindModelsExecute(r ApiFindModelsRequest
|
|||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.source == nil {
|
||||
return localVarReturnValue, nil, reportError("source is required and must be specified")
|
||||
}
|
||||
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "source", r.source, "")
|
||||
if r.source != nil {
|
||||
t := *r.source
|
||||
if reflect.TypeOf(t).Kind() == reflect.Slice {
|
||||
s := reflect.ValueOf(t)
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "source", s.Index(i).Interface(), "multi")
|
||||
}
|
||||
} else {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "source", t, "multi")
|
||||
}
|
||||
}
|
||||
if r.q != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "q", r.q, "")
|
||||
}
|
||||
|
|
@ -422,6 +430,34 @@ type ApiGetAllModelArtifactsRequest struct {
|
|||
ApiService *ModelCatalogServiceAPIService
|
||||
sourceId string
|
||||
modelName string
|
||||
pageSize *string
|
||||
orderBy *OrderByField
|
||||
sortOrder *SortOrder
|
||||
nextPageToken *string
|
||||
}
|
||||
|
||||
// Number of entities in each page.
|
||||
func (r ApiGetAllModelArtifactsRequest) PageSize(pageSize string) ApiGetAllModelArtifactsRequest {
|
||||
r.pageSize = &pageSize
|
||||
return r
|
||||
}
|
||||
|
||||
// Specifies the order by criteria for listing entities.
|
||||
func (r ApiGetAllModelArtifactsRequest) OrderBy(orderBy OrderByField) ApiGetAllModelArtifactsRequest {
|
||||
r.orderBy = &orderBy
|
||||
return r
|
||||
}
|
||||
|
||||
// Specifies the sort order for listing entities, defaults to ASC.
|
||||
func (r ApiGetAllModelArtifactsRequest) SortOrder(sortOrder SortOrder) ApiGetAllModelArtifactsRequest {
|
||||
r.sortOrder = &sortOrder
|
||||
return r
|
||||
}
|
||||
|
||||
// Token to use to retrieve next page of results.
|
||||
func (r ApiGetAllModelArtifactsRequest) NextPageToken(nextPageToken string) ApiGetAllModelArtifactsRequest {
|
||||
r.nextPageToken = &nextPageToken
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiGetAllModelArtifactsRequest) Execute() (*CatalogArtifactList, *http.Response, error) {
|
||||
|
|
@ -469,6 +505,18 @@ func (a *ModelCatalogServiceAPIService) GetAllModelArtifactsExecute(r ApiGetAllM
|
|||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.pageSize != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "pageSize", r.pageSize, "")
|
||||
}
|
||||
if r.orderBy != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "orderBy", r.orderBy, "")
|
||||
}
|
||||
if r.sortOrder != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "sortOrder", r.sortOrder, "")
|
||||
}
|
||||
if r.nextPageToken != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "nextPageToken", r.nextPageToken, "")
|
||||
}
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
Model Catalog REST API
|
||||
|
||||
REST API for Model Registry to create and manage ML model metadata
|
||||
|
||||
API version: v1alpha1
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// checks if the BaseResource type satisfies the MappedNullable interface at compile time
|
||||
var _ MappedNullable = &BaseResource{}
|
||||
|
||||
// BaseResource struct for BaseResource
|
||||
type BaseResource struct {
|
||||
// Output only. Create time of the resource in millisecond since epoch.
|
||||
CreateTimeSinceEpoch *string `json:"createTimeSinceEpoch,omitempty"`
|
||||
// Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
LastUpdateTimeSinceEpoch *string `json:"lastUpdateTimeSinceEpoch,omitempty"`
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
// An optional description about the resource.
|
||||
Description *string `json:"description,omitempty"`
|
||||
// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.
|
||||
Name *string `json:"name,omitempty"`
|
||||
// The unique server generated id of the resource.
|
||||
Id *string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// NewBaseResource instantiates a new BaseResource object
|
||||
// This constructor will assign default values to properties that have it defined,
|
||||
// and makes sure properties required by API are set, but the set of arguments
|
||||
// will change when the set of required properties is changed
|
||||
func NewBaseResource() *BaseResource {
|
||||
this := BaseResource{}
|
||||
return &this
|
||||
}
|
||||
|
||||
// NewBaseResourceWithDefaults instantiates a new BaseResource object
|
||||
// This constructor will only assign default values to properties that have it defined,
|
||||
// but it doesn't guarantee that properties required by API are set
|
||||
func NewBaseResourceWithDefaults() *BaseResource {
|
||||
this := BaseResource{}
|
||||
return &this
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpoch returns the CreateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetCreateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.CreateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpochOk returns a tuple with the CreateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetCreateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CreateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasCreateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasCreateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.CreateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCreateTimeSinceEpoch gets a reference to the given string and assigns it to the CreateTimeSinceEpoch field.
|
||||
func (o *BaseResource) SetCreateTimeSinceEpoch(v string) {
|
||||
o.CreateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpoch returns the LastUpdateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetLastUpdateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpochOk returns a tuple with the LastUpdateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetLastUpdateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.LastUpdateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasLastUpdateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasLastUpdateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetLastUpdateTimeSinceEpoch gets a reference to the given string and assigns it to the LastUpdateTimeSinceEpoch field.
|
||||
func (o *BaseResource) SetLastUpdateTimeSinceEpoch(v string) {
|
||||
o.LastUpdateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetCustomProperties returns the CustomProperties field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetCustomProperties() map[string]MetadataValue {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
var ret map[string]MetadataValue
|
||||
return ret
|
||||
}
|
||||
return *o.CustomProperties
|
||||
}
|
||||
|
||||
// GetCustomPropertiesOk returns a tuple with the CustomProperties field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetCustomPropertiesOk() (*map[string]MetadataValue, bool) {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CustomProperties, true
|
||||
}
|
||||
|
||||
// HasCustomProperties returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasCustomProperties() bool {
|
||||
if o != nil && !IsNil(o.CustomProperties) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCustomProperties gets a reference to the given map[string]MetadataValue and assigns it to the CustomProperties field.
|
||||
func (o *BaseResource) SetCustomProperties(v map[string]MetadataValue) {
|
||||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
// GetDescription returns the Description field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetDescription() string {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Description
|
||||
}
|
||||
|
||||
// GetDescriptionOk returns a tuple with the Description field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetDescriptionOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Description, true
|
||||
}
|
||||
|
||||
// HasDescription returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasDescription() bool {
|
||||
if o != nil && !IsNil(o.Description) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetDescription gets a reference to the given string and assigns it to the Description field.
|
||||
func (o *BaseResource) SetDescription(v string) {
|
||||
o.Description = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *BaseResource) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetName returns the Name field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetName() string {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Name
|
||||
}
|
||||
|
||||
// GetNameOk returns a tuple with the Name field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetNameOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Name, true
|
||||
}
|
||||
|
||||
// HasName returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasName() bool {
|
||||
if o != nil && !IsNil(o.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetName gets a reference to the given string and assigns it to the Name field.
|
||||
func (o *BaseResource) SetName(v string) {
|
||||
o.Name = &v
|
||||
}
|
||||
|
||||
// GetId returns the Id field value if set, zero value otherwise.
|
||||
func (o *BaseResource) GetId() string {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Id
|
||||
}
|
||||
|
||||
// GetIdOk returns a tuple with the Id field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *BaseResource) GetIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Id, true
|
||||
}
|
||||
|
||||
// HasId returns a boolean if a field has been set.
|
||||
func (o *BaseResource) HasId() bool {
|
||||
if o != nil && !IsNil(o.Id) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetId gets a reference to the given string and assigns it to the Id field.
|
||||
func (o *BaseResource) SetId(v string) {
|
||||
o.Id = &v
|
||||
}
|
||||
|
||||
func (o BaseResource) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return json.Marshal(toSerialize)
|
||||
}
|
||||
|
||||
func (o BaseResource) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
if !IsNil(o.CreateTimeSinceEpoch) {
|
||||
toSerialize["createTimeSinceEpoch"] = o.CreateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
toSerialize["lastUpdateTimeSinceEpoch"] = o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
if !IsNil(o.Description) {
|
||||
toSerialize["description"] = o.Description
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
if !IsNil(o.Name) {
|
||||
toSerialize["name"] = o.Name
|
||||
}
|
||||
if !IsNil(o.Id) {
|
||||
toSerialize["id"] = o.Id
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
type NullableBaseResource struct {
|
||||
value *BaseResource
|
||||
isSet bool
|
||||
}
|
||||
|
||||
func (v NullableBaseResource) Get() *BaseResource {
|
||||
return v.value
|
||||
}
|
||||
|
||||
func (v *NullableBaseResource) Set(val *BaseResource) {
|
||||
v.value = val
|
||||
v.isSet = true
|
||||
}
|
||||
|
||||
func (v NullableBaseResource) IsSet() bool {
|
||||
return v.isSet
|
||||
}
|
||||
|
||||
func (v *NullableBaseResource) Unset() {
|
||||
v.value = nil
|
||||
v.isSet = false
|
||||
}
|
||||
|
||||
func NewNullableBaseResource(val *BaseResource) *NullableBaseResource {
|
||||
return &NullableBaseResource{value: val, isSet: true}
|
||||
}
|
||||
|
||||
func (v NullableBaseResource) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.value)
|
||||
}
|
||||
|
||||
func (v *NullableBaseResource) UnmarshalJSON(src []byte) error {
|
||||
v.isSet = true
|
||||
return json.Unmarshal(src, &v.value)
|
||||
}
|
||||
|
|
@ -19,14 +19,22 @@ var _ MappedNullable = &CatalogMetricsArtifact{}
|
|||
|
||||
// CatalogMetricsArtifact A metadata Artifact Entity.
|
||||
type CatalogMetricsArtifact struct {
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
// An optional description about the resource.
|
||||
Description *string `json:"description,omitempty"`
|
||||
// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.
|
||||
Name *string `json:"name,omitempty"`
|
||||
// The unique server generated id of the resource.
|
||||
Id *string `json:"id,omitempty"`
|
||||
// Output only. Create time of the resource in millisecond since epoch.
|
||||
CreateTimeSinceEpoch *string `json:"createTimeSinceEpoch,omitempty"`
|
||||
// Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
LastUpdateTimeSinceEpoch *string `json:"lastUpdateTimeSinceEpoch,omitempty"`
|
||||
ArtifactType string `json:"artifactType"`
|
||||
MetricsType string `json:"metricsType"`
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
}
|
||||
|
||||
// NewCatalogMetricsArtifact instantiates a new CatalogMetricsArtifact object
|
||||
|
|
@ -50,6 +58,166 @@ func NewCatalogMetricsArtifactWithDefaults() *CatalogMetricsArtifact {
|
|||
return &this
|
||||
}
|
||||
|
||||
// GetCustomProperties returns the CustomProperties field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetCustomProperties() map[string]MetadataValue {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
var ret map[string]MetadataValue
|
||||
return ret
|
||||
}
|
||||
return *o.CustomProperties
|
||||
}
|
||||
|
||||
// GetCustomPropertiesOk returns a tuple with the CustomProperties field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetCustomPropertiesOk() (*map[string]MetadataValue, bool) {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CustomProperties, true
|
||||
}
|
||||
|
||||
// HasCustomProperties returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasCustomProperties() bool {
|
||||
if o != nil && !IsNil(o.CustomProperties) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCustomProperties gets a reference to the given map[string]MetadataValue and assigns it to the CustomProperties field.
|
||||
func (o *CatalogMetricsArtifact) SetCustomProperties(v map[string]MetadataValue) {
|
||||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
// GetDescription returns the Description field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetDescription() string {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Description
|
||||
}
|
||||
|
||||
// GetDescriptionOk returns a tuple with the Description field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetDescriptionOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Description, true
|
||||
}
|
||||
|
||||
// HasDescription returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasDescription() bool {
|
||||
if o != nil && !IsNil(o.Description) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetDescription gets a reference to the given string and assigns it to the Description field.
|
||||
func (o *CatalogMetricsArtifact) SetDescription(v string) {
|
||||
o.Description = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *CatalogMetricsArtifact) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetName returns the Name field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetName() string {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Name
|
||||
}
|
||||
|
||||
// GetNameOk returns a tuple with the Name field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetNameOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Name, true
|
||||
}
|
||||
|
||||
// HasName returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasName() bool {
|
||||
if o != nil && !IsNil(o.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetName gets a reference to the given string and assigns it to the Name field.
|
||||
func (o *CatalogMetricsArtifact) SetName(v string) {
|
||||
o.Name = &v
|
||||
}
|
||||
|
||||
// GetId returns the Id field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetId() string {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Id
|
||||
}
|
||||
|
||||
// GetIdOk returns a tuple with the Id field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Id, true
|
||||
}
|
||||
|
||||
// HasId returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasId() bool {
|
||||
if o != nil && !IsNil(o.Id) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetId gets a reference to the given string and assigns it to the Id field.
|
||||
func (o *CatalogMetricsArtifact) SetId(v string) {
|
||||
o.Id = &v
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpoch returns the CreateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetCreateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
|
|
@ -162,38 +330,6 @@ func (o *CatalogMetricsArtifact) SetMetricsType(v string) {
|
|||
o.MetricsType = v
|
||||
}
|
||||
|
||||
// GetCustomProperties returns the CustomProperties field value if set, zero value otherwise.
|
||||
func (o *CatalogMetricsArtifact) GetCustomProperties() map[string]MetadataValue {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
var ret map[string]MetadataValue
|
||||
return ret
|
||||
}
|
||||
return *o.CustomProperties
|
||||
}
|
||||
|
||||
// GetCustomPropertiesOk returns a tuple with the CustomProperties field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogMetricsArtifact) GetCustomPropertiesOk() (*map[string]MetadataValue, bool) {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CustomProperties, true
|
||||
}
|
||||
|
||||
// HasCustomProperties returns a boolean if a field has been set.
|
||||
func (o *CatalogMetricsArtifact) HasCustomProperties() bool {
|
||||
if o != nil && !IsNil(o.CustomProperties) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCustomProperties gets a reference to the given map[string]MetadataValue and assigns it to the CustomProperties field.
|
||||
func (o *CatalogMetricsArtifact) SetCustomProperties(v map[string]MetadataValue) {
|
||||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
func (o CatalogMetricsArtifact) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
|
|
@ -204,6 +340,21 @@ func (o CatalogMetricsArtifact) MarshalJSON() ([]byte, error) {
|
|||
|
||||
func (o CatalogMetricsArtifact) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
if !IsNil(o.Description) {
|
||||
toSerialize["description"] = o.Description
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
if !IsNil(o.Name) {
|
||||
toSerialize["name"] = o.Name
|
||||
}
|
||||
if !IsNil(o.Id) {
|
||||
toSerialize["id"] = o.Id
|
||||
}
|
||||
if !IsNil(o.CreateTimeSinceEpoch) {
|
||||
toSerialize["createTimeSinceEpoch"] = o.CreateTimeSinceEpoch
|
||||
}
|
||||
|
|
@ -212,9 +363,6 @@ func (o CatalogMetricsArtifact) ToMap() (map[string]interface{}, error) {
|
|||
}
|
||||
toSerialize["artifactType"] = o.ArtifactType
|
||||
toSerialize["metricsType"] = o.MetricsType
|
||||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,7 @@ var _ MappedNullable = &CatalogModel{}
|
|||
|
||||
// CatalogModel A model in the model catalog.
|
||||
type CatalogModel struct {
|
||||
// Output only. Create time of the resource in millisecond since epoch.
|
||||
CreateTimeSinceEpoch *string `json:"createTimeSinceEpoch,omitempty"`
|
||||
// Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
LastUpdateTimeSinceEpoch *string `json:"lastUpdateTimeSinceEpoch,omitempty"`
|
||||
// Human-readable description of the model.
|
||||
// An optional description about the resource.
|
||||
Description *string `json:"description,omitempty"`
|
||||
// Model documentation in Markdown.
|
||||
Readme *string `json:"readme,omitempty"`
|
||||
|
|
@ -44,8 +40,16 @@ type CatalogModel struct {
|
|||
LibraryName *string `json:"libraryName,omitempty"`
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// Name of the model. Must be unique within a source.
|
||||
Name string `json:"name"`
|
||||
// The unique server generated id of the resource.
|
||||
Id *string `json:"id,omitempty"`
|
||||
// Output only. Create time of the resource in millisecond since epoch.
|
||||
CreateTimeSinceEpoch *string `json:"createTimeSinceEpoch,omitempty"`
|
||||
// Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
LastUpdateTimeSinceEpoch *string `json:"lastUpdateTimeSinceEpoch,omitempty"`
|
||||
// ID of the source this model belongs to.
|
||||
SourceId *string `json:"source_id,omitempty"`
|
||||
}
|
||||
|
|
@ -68,70 +72,6 @@ func NewCatalogModelWithDefaults() *CatalogModel {
|
|||
return &this
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpoch returns the CreateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetCreateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.CreateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpochOk returns a tuple with the CreateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetCreateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CreateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasCreateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasCreateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.CreateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCreateTimeSinceEpoch gets a reference to the given string and assigns it to the CreateTimeSinceEpoch field.
|
||||
func (o *CatalogModel) SetCreateTimeSinceEpoch(v string) {
|
||||
o.CreateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpoch returns the LastUpdateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetLastUpdateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpochOk returns a tuple with the LastUpdateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetLastUpdateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.LastUpdateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasLastUpdateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasLastUpdateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetLastUpdateTimeSinceEpoch gets a reference to the given string and assigns it to the LastUpdateTimeSinceEpoch field.
|
||||
func (o *CatalogModel) SetLastUpdateTimeSinceEpoch(v string) {
|
||||
o.LastUpdateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetDescription returns the Description field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetDescription() string {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
|
|
@ -484,6 +424,38 @@ func (o *CatalogModel) SetCustomProperties(v map[string]MetadataValue) {
|
|||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *CatalogModel) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetName returns the Name field value
|
||||
func (o *CatalogModel) GetName() string {
|
||||
if o == nil {
|
||||
|
|
@ -508,6 +480,102 @@ func (o *CatalogModel) SetName(v string) {
|
|||
o.Name = v
|
||||
}
|
||||
|
||||
// GetId returns the Id field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetId() string {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Id
|
||||
}
|
||||
|
||||
// GetIdOk returns a tuple with the Id field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Id, true
|
||||
}
|
||||
|
||||
// HasId returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasId() bool {
|
||||
if o != nil && !IsNil(o.Id) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetId gets a reference to the given string and assigns it to the Id field.
|
||||
func (o *CatalogModel) SetId(v string) {
|
||||
o.Id = &v
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpoch returns the CreateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetCreateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.CreateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpochOk returns a tuple with the CreateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetCreateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CreateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasCreateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasCreateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.CreateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCreateTimeSinceEpoch gets a reference to the given string and assigns it to the CreateTimeSinceEpoch field.
|
||||
func (o *CatalogModel) SetCreateTimeSinceEpoch(v string) {
|
||||
o.CreateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpoch returns the LastUpdateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetLastUpdateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
|
||||
// GetLastUpdateTimeSinceEpochOk returns a tuple with the LastUpdateTimeSinceEpoch field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModel) GetLastUpdateTimeSinceEpochOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return nil, false
|
||||
}
|
||||
return o.LastUpdateTimeSinceEpoch, true
|
||||
}
|
||||
|
||||
// HasLastUpdateTimeSinceEpoch returns a boolean if a field has been set.
|
||||
func (o *CatalogModel) HasLastUpdateTimeSinceEpoch() bool {
|
||||
if o != nil && !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetLastUpdateTimeSinceEpoch gets a reference to the given string and assigns it to the LastUpdateTimeSinceEpoch field.
|
||||
func (o *CatalogModel) SetLastUpdateTimeSinceEpoch(v string) {
|
||||
o.LastUpdateTimeSinceEpoch = &v
|
||||
}
|
||||
|
||||
// GetSourceId returns the SourceId field value if set, zero value otherwise.
|
||||
func (o *CatalogModel) GetSourceId() string {
|
||||
if o == nil || IsNil(o.SourceId) {
|
||||
|
|
@ -550,12 +618,6 @@ func (o CatalogModel) MarshalJSON() ([]byte, error) {
|
|||
|
||||
func (o CatalogModel) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
if !IsNil(o.CreateTimeSinceEpoch) {
|
||||
toSerialize["createTimeSinceEpoch"] = o.CreateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
toSerialize["lastUpdateTimeSinceEpoch"] = o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.Description) {
|
||||
toSerialize["description"] = o.Description
|
||||
}
|
||||
|
|
@ -589,7 +651,19 @@ func (o CatalogModel) ToMap() (map[string]interface{}, error) {
|
|||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
toSerialize["name"] = o.Name
|
||||
if !IsNil(o.Id) {
|
||||
toSerialize["id"] = o.Id
|
||||
}
|
||||
if !IsNil(o.CreateTimeSinceEpoch) {
|
||||
toSerialize["createTimeSinceEpoch"] = o.CreateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.LastUpdateTimeSinceEpoch) {
|
||||
toSerialize["lastUpdateTimeSinceEpoch"] = o.LastUpdateTimeSinceEpoch
|
||||
}
|
||||
if !IsNil(o.SourceId) {
|
||||
toSerialize["source_id"] = o.SourceId
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@ var _ MappedNullable = &CatalogModelArtifact{}
|
|||
|
||||
// CatalogModelArtifact A Catalog Model Artifact Entity.
|
||||
type CatalogModelArtifact struct {
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
// An optional description about the resource.
|
||||
Description *string `json:"description,omitempty"`
|
||||
// The external id that come from the clients’ system. This field is optional. If set, it must be unique among all resources within a database instance.
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// The client provided name of the artifact. This field is optional. If set, it must be unique among all the artifacts of the same artifact type within a database instance and cannot be changed once set.
|
||||
Name *string `json:"name,omitempty"`
|
||||
// The unique server generated id of the resource.
|
||||
Id *string `json:"id,omitempty"`
|
||||
// Output only. Create time of the resource in millisecond since epoch.
|
||||
CreateTimeSinceEpoch *string `json:"createTimeSinceEpoch,omitempty"`
|
||||
// Output only. Last update time of the resource since epoch in millisecond since epoch.
|
||||
|
|
@ -26,8 +36,6 @@ type CatalogModelArtifact struct {
|
|||
ArtifactType string `json:"artifactType"`
|
||||
// URI where the model can be retrieved.
|
||||
Uri string `json:"uri"`
|
||||
// User provided custom properties which are not defined by its type.
|
||||
CustomProperties *map[string]MetadataValue `json:"customProperties,omitempty"`
|
||||
}
|
||||
|
||||
// NewCatalogModelArtifact instantiates a new CatalogModelArtifact object
|
||||
|
|
@ -51,6 +59,166 @@ func NewCatalogModelArtifactWithDefaults() *CatalogModelArtifact {
|
|||
return &this
|
||||
}
|
||||
|
||||
// GetCustomProperties returns the CustomProperties field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetCustomProperties() map[string]MetadataValue {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
var ret map[string]MetadataValue
|
||||
return ret
|
||||
}
|
||||
return *o.CustomProperties
|
||||
}
|
||||
|
||||
// GetCustomPropertiesOk returns a tuple with the CustomProperties field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetCustomPropertiesOk() (*map[string]MetadataValue, bool) {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CustomProperties, true
|
||||
}
|
||||
|
||||
// HasCustomProperties returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasCustomProperties() bool {
|
||||
if o != nil && !IsNil(o.CustomProperties) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCustomProperties gets a reference to the given map[string]MetadataValue and assigns it to the CustomProperties field.
|
||||
func (o *CatalogModelArtifact) SetCustomProperties(v map[string]MetadataValue) {
|
||||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
// GetDescription returns the Description field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetDescription() string {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Description
|
||||
}
|
||||
|
||||
// GetDescriptionOk returns a tuple with the Description field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetDescriptionOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Description) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Description, true
|
||||
}
|
||||
|
||||
// HasDescription returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasDescription() bool {
|
||||
if o != nil && !IsNil(o.Description) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetDescription gets a reference to the given string and assigns it to the Description field.
|
||||
func (o *CatalogModelArtifact) SetDescription(v string) {
|
||||
o.Description = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *CatalogModelArtifact) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetName returns the Name field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetName() string {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Name
|
||||
}
|
||||
|
||||
// GetNameOk returns a tuple with the Name field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetNameOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Name) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Name, true
|
||||
}
|
||||
|
||||
// HasName returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasName() bool {
|
||||
if o != nil && !IsNil(o.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetName gets a reference to the given string and assigns it to the Name field.
|
||||
func (o *CatalogModelArtifact) SetName(v string) {
|
||||
o.Name = &v
|
||||
}
|
||||
|
||||
// GetId returns the Id field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetId() string {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.Id
|
||||
}
|
||||
|
||||
// GetIdOk returns a tuple with the Id field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.Id) {
|
||||
return nil, false
|
||||
}
|
||||
return o.Id, true
|
||||
}
|
||||
|
||||
// HasId returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasId() bool {
|
||||
if o != nil && !IsNil(o.Id) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetId gets a reference to the given string and assigns it to the Id field.
|
||||
func (o *CatalogModelArtifact) SetId(v string) {
|
||||
o.Id = &v
|
||||
}
|
||||
|
||||
// GetCreateTimeSinceEpoch returns the CreateTimeSinceEpoch field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetCreateTimeSinceEpoch() string {
|
||||
if o == nil || IsNil(o.CreateTimeSinceEpoch) {
|
||||
|
|
@ -163,38 +331,6 @@ func (o *CatalogModelArtifact) SetUri(v string) {
|
|||
o.Uri = v
|
||||
}
|
||||
|
||||
// GetCustomProperties returns the CustomProperties field value if set, zero value otherwise.
|
||||
func (o *CatalogModelArtifact) GetCustomProperties() map[string]MetadataValue {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
var ret map[string]MetadataValue
|
||||
return ret
|
||||
}
|
||||
return *o.CustomProperties
|
||||
}
|
||||
|
||||
// GetCustomPropertiesOk returns a tuple with the CustomProperties field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *CatalogModelArtifact) GetCustomPropertiesOk() (*map[string]MetadataValue, bool) {
|
||||
if o == nil || IsNil(o.CustomProperties) {
|
||||
return nil, false
|
||||
}
|
||||
return o.CustomProperties, true
|
||||
}
|
||||
|
||||
// HasCustomProperties returns a boolean if a field has been set.
|
||||
func (o *CatalogModelArtifact) HasCustomProperties() bool {
|
||||
if o != nil && !IsNil(o.CustomProperties) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCustomProperties gets a reference to the given map[string]MetadataValue and assigns it to the CustomProperties field.
|
||||
func (o *CatalogModelArtifact) SetCustomProperties(v map[string]MetadataValue) {
|
||||
o.CustomProperties = &v
|
||||
}
|
||||
|
||||
func (o CatalogModelArtifact) MarshalJSON() ([]byte, error) {
|
||||
toSerialize, err := o.ToMap()
|
||||
if err != nil {
|
||||
|
|
@ -205,6 +341,21 @@ func (o CatalogModelArtifact) MarshalJSON() ([]byte, error) {
|
|||
|
||||
func (o CatalogModelArtifact) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
if !IsNil(o.Description) {
|
||||
toSerialize["description"] = o.Description
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
if !IsNil(o.Name) {
|
||||
toSerialize["name"] = o.Name
|
||||
}
|
||||
if !IsNil(o.Id) {
|
||||
toSerialize["id"] = o.Id
|
||||
}
|
||||
if !IsNil(o.CreateTimeSinceEpoch) {
|
||||
toSerialize["createTimeSinceEpoch"] = o.CreateTimeSinceEpoch
|
||||
}
|
||||
|
|
@ -213,9 +364,6 @@ func (o CatalogModelArtifact) ToMap() (map[string]interface{}, error) {
|
|||
}
|
||||
toSerialize["artifactType"] = o.ArtifactType
|
||||
toSerialize["uri"] = o.Uri
|
||||
if !IsNil(o.CustomProperties) {
|
||||
toSerialize["customProperties"] = o.CustomProperties
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -206,7 +206,7 @@ require (
|
|||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ package {{packageName}}
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"{{#apiInfo}}{{#apis}}{{#imports}}
|
||||
"{{import}}"{{/imports}}{{/apis}}{{/apiInfo}}
|
||||
"net/http"
|
||||
|
||||
model "github.com/kubeflow/model-registry/pkg/openapi"
|
||||
)
|
||||
|
|
@ -30,5 +29,5 @@ type {{classname}}Servicer interface { {{#operations}}{{#operation}}
|
|||
{{#isDeprecated}}
|
||||
// Deprecated
|
||||
{{/isDeprecated}}
|
||||
{{operationId}}(context.Context{{#allParams}}, {{^isPrimitiveType}}model.{{/isPrimitiveType}}{{dataType}}{{/allParams}}) (ImplResponse, error){{/operation}}{{/operations}}
|
||||
{{operationId}}(context.Context{{#allParams}}, {{^isPrimitiveType}}{{^isContainer}}model.{{/isContainer}}{{#isContainer}}{{^items.isPrimitiveType}}model.{{/items.isPrimitiveType}}{{/isContainer}}{{/isPrimitiveType}}{{dataType}}{{/allParams}}) (ImplResponse, error){{/operation}}{{/operations}}
|
||||
}{{/apis}}{{/apiInfo}}
|
||||
|
|
|
|||
Loading…
Reference in New Issue