Implement the listing artifact API

Implement the listing artifact API

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2020-01-16 08:04:22 +08:00
parent 8b3313a1ce
commit 19f4bad042
13 changed files with 457 additions and 113 deletions

View File

@ -18,21 +18,52 @@ securityDefinitions:
security: security:
- basicAuth: [] - basicAuth: []
paths: paths:
/projects/{project_id}/repositories/{repository_id}/artifacts/{artifact_id}: /projects/{project_name}/repositories/{repository_name}/artifacts/{digest}:
get: get:
summary: Read artifact by id summary: Get the specific artifact
description: endpoint returns artifact by id description: Get the artifact specified by the digest under the project and repository
tags: tags:
- artifact - artifact
operationId: readArtifact operationId: getArtifact
parameters: parameters:
- $ref: '#/parameters/requestId' - $ref: '#/parameters/requestId'
- $ref: '#/parameters/projectId' - $ref: '#/parameters/projectName'
- $ref: '#/parameters/repositoryId' - $ref: '#/parameters/repositoryName'
- $ref: '#/parameters/artifactId' - $ref: '#/parameters/digest'
- name: with_tag
in: query
description: Specify whether the tags are inclued inside the returning artifacts
type: boolean
required: false
default: true
- name: with_label
in: query
description: Specify whether the labels are inclued inside the returning artifacts
type: boolean
required: false
default: false
- name: with_scan_overview
in: query
description: Specify whether the scan overview is inclued inside the returning artifacts
type: boolean
required: false
default: false
# should be in tag level
- name: with_signatrue
in: query
description: Specify whether the signature is inclued inside the returning artifacts
type: boolean
required: false
default: false
- name: with_immutable_status
in: query
description: Specify whether the immutable status is inclued inside the tags of the returning artifacts. Only works when setting "with_tag=true"
type: boolean
required: false
default: false
responses: responses:
'200': '200':
description: OK description: Success
schema: schema:
$ref: '#/definitions/Artifact' $ref: '#/definitions/Artifact'
'401': '401':
@ -42,59 +73,78 @@ paths:
'500': '500':
$ref: '#/responses/500' $ref: '#/responses/500'
delete: delete:
summary: Delete artifact by id summary: Delete the specific artifact
description: endpoint to delete the artifact by id description: Delete the artifact specified by the digest under the project and repository
tags: tags:
- artifact - artifact
operationId: deleteArtifact operationId: deleteArtifact
parameters: parameters:
- $ref: '#/parameters/requestId' - $ref: '#/parameters/requestId'
- $ref: '#/parameters/projectId' - $ref: '#/parameters/projectName'
- $ref: '#/parameters/repositoryId' - $ref: '#/parameters/repositoryName'
- $ref: '#/parameters/artifactId' - $ref: '#/parameters/digest'
responses: responses:
'200': '200':
description: Artifact is deleted successfully. description: Success
'401': '401':
$ref: '#/responses/401' $ref: '#/responses/401'
'403': '403':
$ref: '#/responses/403' $ref: '#/responses/403'
'500': '500':
$ref: '#/responses/500' $ref: '#/responses/500'
/projects/{project_id}/repositories/{repository_id}/artifacts: /projects/{project_name}/repositories/{repository_name}/artifacts:
get: get:
summary: List artifacts of the repository summary: List artifacts
description: endpoint returns all artifacts of the repository. description: List artifacts under the specific project and repository.
tags: tags:
- artifact - artifact
operationId: listArtifacts operationId: listArtifacts
parameters: parameters:
- $ref: '#/parameters/requestId' - $ref: '#/parameters/requestId'
- $ref: '#/parameters/projectId' - $ref: '#/parameters/projectName'
- $ref: '#/parameters/repositoryId' - $ref: '#/parameters/repositoryName'
- $ref: '#/parameters/page' - $ref: '#/parameters/page'
- $ref: '#/parameters/pageSize' - $ref: '#/parameters/pageSize'
- name: label - name: type
in: query in: query
description: Response for artifact include label info when it's true description: Query the artifacts by type. Valid values can be "IMAGE", "CHART", etc.
type: string
required: false
- name: with_tag
in: query
description: Specify whether the tags are inclued inside the returning artifacts
type: boolean
required: false
default: true
- name: with_label
in: query
description: Specify whether the labels are inclued inside the returning artifacts
type: boolean type: boolean
required: false required: false
default: false default: false
- name: signature - name: with_scan_overview
in: query in: query
description: Response for artifact include signature info when it's true description: Specify whether the scan overview is inclued inside the returning artifacts
type: boolean type: boolean
required: false required: false
default: false default: false
- name: vulnerability # should be in tag level
- name: with_signatrue
in: query in: query
description: Response for artifact include vulnerability info when it's true description: Specify whether the signature is inclued inside the returning artifacts
type: boolean type: boolean
required: false required: false
default: false default: false
- name: with_immutable_status
in: query
description: Specify whether the immutable status is inclued inside the tags of the returning artifacts. Only works when setting "with_tag=true"
type: boolean
required: false
default: false
# TODO add other query string: type, ....
responses: responses:
'200': '200':
description: OK description: Success
headers: headers:
X-Total-Count: X-Total-Count:
description: The total count of artifacts description: The total count of artifacts
@ -115,70 +165,67 @@ paths:
parameters: parameters:
requestId: requestId:
name: X-Request-Id name: X-Request-Id
description: A unique id for the request description: An unique ID for the request
in: header in: header
type: string type: string
required: false required: false
minLength: 1 minLength: 1
projectId: projectName:
name: project_id name: project_name
in: path in: path
description: The id of the project description: The name of the project
required: true required: true
type: integer type: string
format: int64 repositoryName:
repositoryId: name: repository_name
name: repository_id
in: path in: path
description: The id of the repository description: The name of the repository
required: true required: true
type: integer type: string
format: int64 digest:
artifactId: name: digest
name: artifact_id
in: path in: path
description: The id of the artifact description: The digest of the artifact
required: true required: true
type: integer type: string
format: int64
page: page:
name: page name: page
in: query in: query
type: integer type: integer
format: int32 format: int64
required: false required: false
description: 'The page number, default is 1.' description: The page number
default: 1 default: 1
pageSize: pageSize:
name: page_size name: page_size
in: query in: query
type: integer type: integer
format: int32 format: int64
required: false required: false
description: 'The size of per page, default is 10, maximum is 100.' description: The size of per page
default: 10 default: 10
responses: responses:
'401': '401':
description: UnauthorizedError description: Unauthorized
headers: headers:
X-Request-Id: X-Request-Id:
description: The request id this is a response to description: The ID of the corresponding request for the response
type: string type: string
schema: schema:
$ref: '#/definitions/Error' $ref: '#/definitions/Error'
'403': '403':
description: ForbiddenError description: Forbidden
headers: headers:
X-Request-Id: X-Request-Id:
description: The request id this is a response to description: The ID of the corresponding request for the response
type: string type: string
schema: schema:
$ref: '#/definitions/Error' $ref: '#/definitions/Error'
'500': '500':
description: InternalServerError description: Internal server error
headers: headers:
X-Request-Id: X-Request-Id:
description: The request id this is a response to description: The ID of the corresponding request for the response
type: string type: string
schema: schema:
$ref: '#/definitions/Error' $ref: '#/definitions/Error'
@ -204,40 +251,132 @@ definitions:
id: id:
type: integer type: integer
format: int64 format: int64
description: The id of the artifact description: The ID of the artifact
type: type:
type: string type: string
description: The type of the artifact, eg image, chart, etc description: The type of the artifact, e.g. image, chart, etc
repository:
$ref: '#/definitions/Repository'
description: The repository of the artifact
tags:
description: The list of tags that attached to the artifact
media_type: media_type:
type: string type: string
description: The specific media type for the artifact description: The media type of the artifact
manifest_media_type:
type: string
description: The manifest media type of the artifact
project_id:
type: integer
format: int64
description: The ID of the project that the artifact belongs to
repository_id:
type: integer
format: int64
description: The ID of the repository that the artifact belongs to
digest: digest:
type: string type: string
description: The digest of the artifact description: The digest of the artifact
size: size:
type: string type: integer
format: int64 format: int64
description: The size of the artifact description: The size of the artifact
upload_time: push_time:
type: string type: string
format: date-time format: date-time
description: The upload time for the artifact description: The push time of the artifact
labels: pull_time:
description: The list of labels that attached to the artifact type: string
signature: format: date-time
description: The signature attached to the artifact description: The latest pull time of the artifact
Repository: extra_attrs:
$ref: '#/definitions/ExtraAttrs'
annotations:
$ref: '#/definitions/Annotations'
references:
type: array
items:
$ref: '#/definitions/Reference'
tags:
type: array
items:
$ref: '#/definitions/Tag'
sub_resource_links:
$ref: '#/definitions/SubResourceLinks'
Tag:
type: object type: object
properties: properties:
id: id:
type: integer type: integer
format: int64 format: int64
description: The id of the repository description: The ID of the tag
repository_id:
type: integer
format: int64
description: The ID of the repository that the tag belongs to
artifact_id:
type: integer
format: int64
description: The ID of the artifact that the tag attached to
name: name:
type: string type: string
description: The name of repository. description: The name of the tag
push_time:
type: string
format: date-time
description: The push time of the tag
pull_time:
type: string
format: date-time
description: The latest pull time of the tag
ExtraAttrs:
type: object
additionalProperties:
type: object
Annotations:
type: object
additionalProperties:
type: string
SubResourceLinks:
type: object
additionalProperties:
type: array
items:
$ref: '#/definitions/ResourceLink'
ResourceLink:
type: object
properties:
href:
type: string
description: The link of the resource
absolute:
type: boolean
description: Determine whether the link is an absolute URL or not
Reference:
type: object
properties:
parent_id:
type: integer
format: int64
description: The parent ID of the reference
child_id:
type: integer
format: int64
description: The child ID of the reference
platform:
$ref: '#/definitions/Platform'
Platform:
type: object
properties:
architecture:
type: string
description: The architecture that the artifact applys to
os:
type: string
description: The OS that the artifact applys to
os.version:
type: string
description: The version of the OS that the artifact applys to
os.features:
type: array
description: The features of the OS that the artifact applys to
items:
type: string
variant:
type: string
description: The variant of the CPU

View File

@ -296,7 +296,13 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
log.Errorf("failed to list tag of artifact %d: %v", artifact.ID, err) log.Errorf("failed to list tag of artifact %d: %v", artifact.ID, err)
} }
} }
// TODO populate other properties: scan, signature etc. if option.WithLabel {
// TODO populate label
}
if option.WithScanOverview {
// TODO populate scan overview
}
// TODO populate signature on artifact or label level?
return artifact return artifact
} }
@ -308,6 +314,9 @@ func (c *controller) assembleTag(ctx context.Context, tag *tm.Tag, option *TagOp
if option == nil { if option == nil {
return t return t
} }
// TODO populate label, signature, immutable status for tag if option.WithImmutableStatus {
// TODO populate immutable status
}
// TODO populate signature on tag level?
return t return t
} }

View File

@ -68,7 +68,6 @@ func (c *controllerTestSuite) TestAssembleTag() {
PullTime: time.Now(), PullTime: time.Now(),
} }
option := &TagOption{ option := &TagOption{
WithLabel: true,
WithImmutableStatus: true, WithImmutableStatus: true,
} }
@ -85,11 +84,12 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
option := &Option{ option := &Option{
WithTag: true, WithTag: true,
TagOption: &TagOption{ TagOption: &TagOption{
WithLabel: false,
WithImmutableStatus: false, WithImmutableStatus: false,
}, },
WithScanResult: true, WithLabel: false,
WithSignature: true, WithScanOverview: true,
WithSignature: true,
} }
tg := &tag.Tag{ tg := &tag.Tag{
ID: 1, ID: 1,
@ -209,9 +209,9 @@ func (c *controllerTestSuite) TestEnsure() {
func (c *controllerTestSuite) TestList() { func (c *controllerTestSuite) TestList() {
query := &q.Query{} query := &q.Query{}
option := &Option{ option := &Option{
WithTag: true, WithTag: true,
WithScanResult: true, WithScanOverview: true,
WithSignature: true, WithSignature: true,
} }
c.artMgr.On("List").Return(1, []*artifact.Artifact{ c.artMgr.On("List").Return(1, []*artifact.Artifact{
{ {

View File

@ -15,8 +15,10 @@
package artifact package artifact
import ( import (
"github.com/go-openapi/strfmt"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/tag/model/tag" "github.com/goharbor/harbor/src/pkg/tag/model/tag"
"github.com/goharbor/harbor/src/server/v2.0/models"
) )
// Artifact is the overall view of artifact // Artifact is the overall view of artifact
@ -28,6 +30,62 @@ type Artifact struct {
// TODO add other attrs: signature, scan result, etc // TODO add other attrs: signature, scan result, etc
} }
// ToSwagger converts the artifact to the swagger model
func (a *Artifact) ToSwagger() *models.Artifact {
art := &models.Artifact{
ID: a.ID,
Type: a.Type,
MediaType: a.MediaType,
ManifestMediaType: a.ManifestMediaType,
ProjectID: a.ProjectID,
RepositoryID: a.RepositoryID,
Digest: a.Digest,
Size: a.Size,
PullTime: strfmt.DateTime(a.PullTime),
PushTime: strfmt.DateTime(a.PushTime),
ExtraAttrs: a.ExtraAttrs,
Annotations: a.Annotations,
}
for _, reference := range a.References {
ref := &models.Reference{
ChildID: reference.ChildID,
ParentID: reference.ParentID,
}
if reference.Platform != nil {
ref.Platform = &models.Platform{
Architecture: reference.Platform.Architecture,
Os: reference.Platform.OS,
OsFeatures: reference.Platform.OSFeatures,
OsVersion: reference.Platform.OSVersion,
Variant: reference.Platform.Variant,
}
}
art.References = append(art.References, ref)
}
for _, tag := range a.Tags {
art.Tags = append(art.Tags, &models.Tag{
ArtifactID: tag.ArtifactID,
ID: tag.ID,
Name: tag.Name,
PullTime: strfmt.DateTime(tag.PullTime),
PushTime: strfmt.DateTime(tag.PushTime),
RepositoryID: tag.RepositoryID,
})
}
for resource, links := range a.SubResourceLinks {
for _, link := range links {
art.SubResourceLinks[resource] = []models.ResourceLink{}
if link != nil {
art.SubResourceLinks[resource] = append(art.SubResourceLinks[resource], models.ResourceLink{
Absolute: link.Absolute,
Href: link.HREF,
})
}
}
}
return art
}
// Tag is the overall view of tag // Tag is the overall view of tag
type Tag struct { type Tag struct {
tag.Tag tag.Tag
@ -48,15 +106,16 @@ type ResourceLink struct {
// Option is used to specify the properties returned when listing/getting artifacts // Option is used to specify the properties returned when listing/getting artifacts
type Option struct { type Option struct {
WithTag bool WithTag bool
TagOption *TagOption // only works when WithTag is set to true TagOption *TagOption // only works when WithTag is set to true
WithScanResult bool WithLabel bool
WithSignature bool // TODO move it to TagOption? WithScanOverview bool
// TODO move it to TagOption?
WithSignature bool
} }
// TagOption is used to specify the properties returned when listing/getting tags // TagOption is used to specify the properties returned when listing/getting tags
type TagOption struct { type TagOption struct {
WithLabel bool
WithImmutableStatus bool WithImmutableStatus bool
} }

View File

@ -111,7 +111,8 @@ func (r *Reference) From(ref *dao.ArtifactReference) {
r.ParentID = ref.ParentID r.ParentID = ref.ParentID
r.ChildID = ref.ChildID r.ChildID = ref.ChildID
if len(ref.Platform) > 0 { if len(ref.Platform) > 0 {
if err := json.Unmarshal([]byte(ref.Platform), r); err != nil { r.Platform = &v1.Platform{}
if err := json.Unmarshal([]byte(ref.Platform), r.Platform); err != nil {
log.Errorf("failed to unmarshal the platform of reference: %v", err) log.Errorf("failed to unmarshal the platform of reference: %v", err)
} }
} }

View File

@ -17,6 +17,7 @@ package repository
import ( import (
"context" "context"
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/q" "github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/repository/dao" "github.com/goharbor/harbor/src/pkg/repository/dao"
) )
@ -30,6 +31,8 @@ type Manager interface {
List(ctx context.Context, query *q.Query) (total int64, repositories []*models.RepoRecord, err error) List(ctx context.Context, query *q.Query) (total int64, repositories []*models.RepoRecord, err error)
// Get the repository specified by ID // Get the repository specified by ID
Get(ctx context.Context, id int64) (repository *models.RepoRecord, err error) Get(ctx context.Context, id int64) (repository *models.RepoRecord, err error)
// GetByName gets the repository specified by name
GetByName(ctx context.Context, name string) (repository *models.RepoRecord, err error)
// Create a repository // Create a repository
Create(ctx context.Context, repository *models.RepoRecord) (id int64, err error) Create(ctx context.Context, repository *models.RepoRecord) (id int64, err error)
// Delete the repository specified by ID // Delete the repository specified by ID
@ -65,6 +68,22 @@ func (m *manager) Get(ctx context.Context, id int64) (*models.RepoRecord, error)
return m.dao.Get(ctx, id) return m.dao.Get(ctx, id)
} }
func (m *manager) GetByName(ctx context.Context, name string) (repository *models.RepoRecord, err error) {
_, repositories, err := m.List(ctx, &q.Query{
Keywords: map[string]interface{}{
"Name": name,
},
})
if err != nil {
return nil, err
}
if len(repositories) == 0 {
return nil, ierror.New(nil).WithCode(ierror.NotFoundCode).
WithMessage("repository %s not found", name)
}
return repositories[0], nil
}
func (m *manager) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) { func (m *manager) Create(ctx context.Context, repository *models.RepoRecord) (int64, error) {
return m.dao.Create(ctx, repository) return m.dao.Create(ctx, repository)
} }

View File

@ -95,6 +95,21 @@ func (m *managerTestSuite) TestGet() {
m.Equal(repository.RepositoryID, repo.RepositoryID) m.Equal(repository.RepositoryID, repo.RepositoryID)
} }
func (m *managerTestSuite) TestGetByName() {
repository := &models.RepoRecord{
RepositoryID: 1,
ProjectID: 1,
Name: "library/hello-world",
}
m.dao.On("Count", mock.Anything).Return(1, nil)
m.dao.On("List", mock.Anything).Return([]*models.RepoRecord{repository}, nil)
repo, err := m.mgr.GetByName(nil, "library/hello-world")
m.Require().Nil(err)
m.dao.AssertExpectations(m.T())
m.Require().NotNil(repo)
m.Equal(repository.RepositoryID, repo.RepositoryID)
}
func (m *managerTestSuite) TestCreate() { func (m *managerTestSuite) TestCreate() {
m.dao.On("Create", mock.Anything).Return(1, nil) m.dao.On("Create", mock.Anything).Return(1, nil)
id, err := m.mgr.Create(nil, &models.RepoRecord{ id, err := m.mgr.Create(nil, &models.RepoRecord{

View File

@ -16,32 +16,98 @@ package handler
import ( import (
"context" "context"
"fmt"
"github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/api/artifact"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/goharbor/harbor/src/server/v2.0/models"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/artifact" operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/artifact"
) )
// ArtifactAPI the api implemention of artifacts func newArtifactAPI() *artifactAPI {
type ArtifactAPI struct { return &artifactAPI{
BaseAPI artCtl: artifact.Ctl,
proMgr: project.Mgr,
repoMgr: repository.Mgr,
}
} }
// DeleteArtifact ... type artifactAPI struct {
func (api *ArtifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder { BaseAPI
artCtl artifact.Controller
proMgr project.Manager
repoMgr repository.Manager
}
// TODO do auth in a separate middleware
func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListArtifactsParams) middleware.Responder {
// set query
query := &q.Query{
Keywords: map[string]interface{}{},
}
if params.Type != nil {
query.Keywords["Type"] = *(params.Type)
}
if params.Page != nil {
query.PageNumber = *(params.Page)
}
if params.PageSize != nil {
query.PageSize = *(params.PageSize)
}
repository, err := a.repoMgr.GetByName(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName))
if err != nil {
return a.SendError(ctx, err)
}
query.Keywords["RepositoryID"] = repository.RepositoryID
// set option
option := &artifact.Option{
WithTag: true, // return the tag by default
}
if params.WithTag != nil {
option.WithTag = *(params.WithTag)
}
if option.WithTag {
if params.WithImmutableStatus != nil {
option.TagOption = &artifact.TagOption{
WithImmutableStatus: *(params.WithImmutableStatus),
}
}
}
if params.WithLabel != nil {
option.WithLabel = *(params.WithLabel)
}
if params.WithScanOverview != nil {
option.WithScanOverview = *(params.WithScanOverview)
}
if params.WithSignatrue != nil {
option.WithSignature = *(params.WithSignatrue)
}
// list artifacts according to the query and option
total, arts, err := a.artCtl.List(ctx, query, option)
if err != nil {
return a.SendError(ctx, err)
}
var artifacts []*models.Artifact
for _, art := range arts {
artifacts = append(artifacts, art.ToSwagger())
}
// TODO add link header
return operation.NewListArtifactsOK().WithXTotalCount(total).WithLink("").WithPayload(artifacts)
}
func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtifactParams) middleware.Responder {
// TODO implement
return operation.NewGetArtifactOK()
}
func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder {
// TODO implement
return operation.NewDeleteArtifactOK() return operation.NewDeleteArtifactOK()
} }
// ListArtifacts ...
func (api *ArtifactAPI) ListArtifacts(ctx context.Context, params operation.ListArtifactsParams) middleware.Responder {
return operation.NewListArtifactsOK()
}
// ReadArtifact ...
func (api *ArtifactAPI) ReadArtifact(ctx context.Context, params operation.ReadArtifactParams) middleware.Responder {
return operation.NewReadArtifactOK()
}
// NewArtifactAPI returns API of artifacts
func NewArtifactAPI() *ArtifactAPI {
return &ArtifactAPI{}
}

View File

@ -14,6 +14,8 @@
package handler package handler
// TODO move this file out of v2.0 folder as this is common for all versions of API
import ( import (
"context" "context"

View File

@ -30,7 +30,7 @@ import (
// New returns http handler for API V2.0 // New returns http handler for API V2.0
func New() http.Handler { func New() http.Handler {
h, api, err := restapi.HandlerAPI(restapi.Config{ h, api, err := restapi.HandlerAPI(restapi.Config{
ArtifactAPI: NewArtifactAPI(), ArtifactAPI: newArtifactAPI(),
}) })
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -30,13 +30,21 @@ type FakeArtifactManager struct {
// List ... // List ...
func (f *FakeArtifactManager) List(ctx context.Context, query *q.Query) (int64, []*artifact.Artifact, error) { func (f *FakeArtifactManager) List(ctx context.Context, query *q.Query) (int64, []*artifact.Artifact, error) {
args := f.Called() args := f.Called()
return int64(args.Int(0)), args.Get(1).([]*artifact.Artifact), args.Error(2) var artifacts []*artifact.Artifact
if args.Get(1) != nil {
artifacts = args.Get(1).([]*artifact.Artifact)
}
return int64(args.Int(0)), artifacts, args.Error(2)
} }
// Get ... // Get ...
func (f *FakeArtifactManager) Get(ctx context.Context, id int64) (*artifact.Artifact, error) { func (f *FakeArtifactManager) Get(ctx context.Context, id int64) (*artifact.Artifact, error) {
args := f.Called() args := f.Called()
return args.Get(0).(*artifact.Artifact), args.Error(1) var art *artifact.Artifact
if args.Get(0) != nil {
art = args.Get(0).(*artifact.Artifact)
}
return art, args.Error(1)
} }
// Create ... // Create ...

View File

@ -29,13 +29,31 @@ type FakeRepositoryManager struct {
// List ... // List ...
func (f *FakeRepositoryManager) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) { func (f *FakeRepositoryManager) List(ctx context.Context, query *q.Query) (int64, []*models.RepoRecord, error) {
args := f.Called() args := f.Called()
return int64(args.Int(0)), args.Get(1).([]*models.RepoRecord), args.Error(2) var repositories []*models.RepoRecord
if args.Get(1) != nil {
repositories = args.Get(1).([]*models.RepoRecord)
}
return int64(args.Int(0)), repositories, args.Error(2)
} }
// Get ... // Get ...
func (f *FakeRepositoryManager) Get(ctx context.Context, id int64) (*models.RepoRecord, error) { func (f *FakeRepositoryManager) Get(ctx context.Context, id int64) (*models.RepoRecord, error) {
args := f.Called() args := f.Called()
return args.Get(0).(*models.RepoRecord), args.Error(1) var repository *models.RepoRecord
if args.Get(0) != nil {
repository = args.Get(0).(*models.RepoRecord)
}
return repository, args.Error(1)
}
// GetByName ...
func (f *FakeRepositoryManager) GetByName(ctx context.Context, name string) (*models.RepoRecord, error) {
args := f.Called()
var repository *models.RepoRecord
if args.Get(0) != nil {
repository = args.Get(0).(*models.RepoRecord)
}
return repository, args.Error(1)
} }
// Delete ... // Delete ...

View File

@ -29,13 +29,21 @@ type FakeTagManager struct {
// List ... // List ...
func (f *FakeTagManager) List(ctx context.Context, query *q.Query) (int64, []*tag.Tag, error) { func (f *FakeTagManager) List(ctx context.Context, query *q.Query) (int64, []*tag.Tag, error) {
args := f.Called() args := f.Called()
return int64(args.Int(0)), args.Get(1).([]*tag.Tag), args.Error(2) var tags []*tag.Tag
if args.Get(1) != nil {
tags = args.Get(1).([]*tag.Tag)
}
return int64(args.Int(0)), tags, args.Error(2)
} }
// Get ... // Get ...
func (f *FakeTagManager) Get(ctx context.Context, id int64) (*tag.Tag, error) { func (f *FakeTagManager) Get(ctx context.Context, id int64) (*tag.Tag, error) {
args := f.Called() args := f.Called()
return args.Get(0).(*tag.Tag), args.Error(1) var tg *tag.Tag
if args.Get(0) != nil {
tg = args.Get(0).(*tag.Tag)
}
return tg, args.Error(1)
} }
// Create ... // Create ...