mirror of
https://github.com/goharbor/harbor
synced 2025-04-15 19:18:14 +00:00
Api refactor label (#14650)
* Refactor labl api move to the new program model Signed-off-by: wang yan <wangyan@vmware.com> * continue resolve review comments Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
45663e002d
commit
6e3c9e29df
|
@ -180,173 +180,6 @@ paths:
|
|||
description: User need to log in first.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/labels:
|
||||
get:
|
||||
summary: List labels according to the query strings.
|
||||
description: |
|
||||
This endpoint let user list labels by name, scope and project_id
|
||||
parameters:
|
||||
- name: name
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The label name.
|
||||
- name: scope
|
||||
in: query
|
||||
type: string
|
||||
required: true
|
||||
description: The label scope. Valid values are g and p. g for global labels and p for project labels.
|
||||
- name: project_id
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: 'Relevant project ID, required when scope is p.'
|
||||
- name: page
|
||||
in: query
|
||||
type: integer
|
||||
format: int32
|
||||
required: false
|
||||
description: The page number.
|
||||
- name: page_size
|
||||
in: query
|
||||
type: integer
|
||||
format: int32
|
||||
required: false
|
||||
description: The size of per page.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Get successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Label'
|
||||
headers:
|
||||
X-Total-Count:
|
||||
description: The total count of available items
|
||||
type: integer
|
||||
Link:
|
||||
description: Link to previous page and next page
|
||||
type: string
|
||||
'400':
|
||||
description: Invalid parameters.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
post:
|
||||
summary: Post creates a label
|
||||
description: |
|
||||
This endpoint let user creates a label.
|
||||
parameters:
|
||||
- name: label
|
||||
in: body
|
||||
description: The json object of label.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'201':
|
||||
description: Create successfully.
|
||||
headers:
|
||||
Location:
|
||||
type: string
|
||||
description: The URL of the created resource
|
||||
'400':
|
||||
description: Invalid parameters.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'409':
|
||||
description: Label with the same name and same scope already exists.
|
||||
'415':
|
||||
$ref: '#/responses/UnsupportedMediaType'
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/labels/{id}':
|
||||
get:
|
||||
summary: Get the label specified by ID.
|
||||
description: |
|
||||
This endpoint let user get the label by specific ID.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Label ID
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Get successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'404':
|
||||
description: The resource does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
put:
|
||||
summary: Update the label properties.
|
||||
description: |
|
||||
This endpoint let user update label properties.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Label ID
|
||||
- name: label
|
||||
in: body
|
||||
description: The updated label json object.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Update successfully.
|
||||
'400':
|
||||
description: Invalid parameters.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'404':
|
||||
description: The resource does not exist.
|
||||
'409':
|
||||
description: The label with the same name already exists.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
delete:
|
||||
summary: Delete the label specified by ID.
|
||||
description: |
|
||||
Delete the label specified by ID.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Label ID
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Delete successfully.
|
||||
'400':
|
||||
description: Invalid parameters.
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'404':
|
||||
description: The resource does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/email/ping:
|
||||
post:
|
||||
summary: Test connection and authentication with email server.
|
||||
|
|
|
@ -4815,6 +4815,158 @@ paths:
|
|||
description: The auth mode of the system is not "oidc_auth", or the user is not onboarded via OIDC AuthN.
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
|
||||
/labels:
|
||||
get:
|
||||
summary: List labels according to the query strings.
|
||||
description: |
|
||||
This endpoint let user list labels by name, scope and project_id
|
||||
tags:
|
||||
- label
|
||||
operationId: ListLabels
|
||||
parameters:
|
||||
- $ref: '#/parameters/requestId'
|
||||
- $ref: '#/parameters/query'
|
||||
- $ref: '#/parameters/sort'
|
||||
- $ref: '#/parameters/page'
|
||||
- $ref: '#/parameters/pageSize'
|
||||
- name: name
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The label name.
|
||||
- name: scope
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The label scope. Valid values are g and p. g for global labels and p for project labels.
|
||||
- name: project_id
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: Relevant project ID, required when scope is p.
|
||||
responses:
|
||||
'200':
|
||||
description: Get successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Label'
|
||||
headers:
|
||||
X-Total-Count:
|
||||
description: The total count of available items
|
||||
type: integer
|
||||
Link:
|
||||
description: Link to previous page and next page
|
||||
type: string
|
||||
'400':
|
||||
$ref: '#/responses/400'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
post:
|
||||
summary: Post creates a label
|
||||
description: |
|
||||
This endpoint let user creates a label.
|
||||
tags:
|
||||
- label
|
||||
operationId: CreateLabel
|
||||
parameters:
|
||||
- name: label
|
||||
in: body
|
||||
description: The json object of label.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
responses:
|
||||
'201':
|
||||
description: Create successfully.
|
||||
headers:
|
||||
Location:
|
||||
type: string
|
||||
description: The URL of the created resource
|
||||
'400':
|
||||
$ref: '#/responses/400'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'409':
|
||||
$ref: '#/responses/409'
|
||||
'415':
|
||||
$ref: '#/responses/415'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
'/labels/{label_id}':
|
||||
get:
|
||||
summary: Get the label specified by ID.
|
||||
description: |
|
||||
This endpoint let user get the label by specific ID.
|
||||
tags:
|
||||
- label
|
||||
operationId: GetLabelByID
|
||||
parameters:
|
||||
- $ref: '#/parameters/labelId'
|
||||
responses:
|
||||
'200':
|
||||
description: Get successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'404':
|
||||
$ref: '#/responses/404'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
put:
|
||||
summary: Update the label properties.
|
||||
description: |
|
||||
This endpoint let user update label properties.
|
||||
tags:
|
||||
- label
|
||||
operationId: UpdateLabel
|
||||
parameters:
|
||||
- $ref: '#/parameters/labelId'
|
||||
- name: label
|
||||
in: body
|
||||
description: The updated label json object.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/Label'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/responses/200'
|
||||
'400':
|
||||
$ref: '#/responses/400'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'404':
|
||||
$ref: '#/responses/404'
|
||||
'409':
|
||||
$ref: '#/responses/409'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
delete:
|
||||
summary: Delete the label specified by ID.
|
||||
description: |
|
||||
Delete the label specified by ID.
|
||||
tags:
|
||||
- label
|
||||
operationId: DeleteLabel
|
||||
parameters:
|
||||
- $ref: '#/parameters/labelId'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/responses/200'
|
||||
'400':
|
||||
$ref: '#/responses/400'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'404':
|
||||
$ref: '#/responses/404'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
|
||||
parameters:
|
||||
query:
|
||||
name: q
|
||||
|
@ -4947,6 +5099,13 @@ parameters:
|
|||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
labelId:
|
||||
name: label_id
|
||||
in: path
|
||||
description: Label ID
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
webhookPolicyId:
|
||||
name: webhook_policy_id
|
||||
in: path
|
||||
|
@ -5040,6 +5199,14 @@ responses:
|
|||
type: string
|
||||
schema:
|
||||
$ref: '#/definitions/Errors'
|
||||
'415':
|
||||
description: Unsupported MediaType
|
||||
headers:
|
||||
X-Request-Id:
|
||||
description: The ID of the corresponding request for the response
|
||||
type: string
|
||||
schema:
|
||||
$ref: '#/definitions/Errors'
|
||||
'500':
|
||||
description: Internal server error
|
||||
headers:
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
hlog "github.com/goharbor/harbor/src/lib/log"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
|
@ -27,7 +27,7 @@ const (
|
|||
// ChartVersion extends the helm ChartVersion with additional labels
|
||||
type ChartVersion struct {
|
||||
helm_repo.ChartVersion
|
||||
Labels []*models.Label `json:"labels"`
|
||||
Labels []*model.Label `json:"labels"`
|
||||
}
|
||||
|
||||
// ChartVersions is an array of extended ChartVersion
|
||||
|
@ -40,7 +40,7 @@ type ChartVersionDetails struct {
|
|||
Values map[string]interface{} `json:"values"`
|
||||
Files map[string]string `json:"files"`
|
||||
Security *SecurityReport `json:"security"`
|
||||
Labels []*models.Label `json:"labels"`
|
||||
Labels []*model.Label `json:"labels"`
|
||||
}
|
||||
|
||||
// SecurityReport keeps the info related with security
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
libOrm "github.com/goharbor/harbor/src/lib/orm"
|
||||
)
|
||||
|
||||
// AddLabel creates a label
|
||||
func AddLabel(label *models.Label) (int64, error) {
|
||||
now := time.Now()
|
||||
label.CreationTime = now
|
||||
label.UpdateTime = now
|
||||
return GetOrmer().Insert(label)
|
||||
}
|
||||
|
||||
// GetLabel specified by ID
|
||||
func GetLabel(id int64) (*models.Label, error) {
|
||||
label := &models.Label{
|
||||
ID: id,
|
||||
}
|
||||
if err := GetOrmer().Read(label); err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
||||
|
||||
// GetTotalOfLabels returns the total count of labels
|
||||
func GetTotalOfLabels(query *models.LabelQuery) (int64, error) {
|
||||
qs := getLabelQuerySetter(query)
|
||||
return qs.Count()
|
||||
}
|
||||
|
||||
// ListLabels list labels according to the query conditions
|
||||
func ListLabels(query *models.LabelQuery) ([]*models.Label, error) {
|
||||
qs := getLabelQuerySetter(query)
|
||||
if query.Size > 0 {
|
||||
qs = qs.Limit(query.Size)
|
||||
if query.Page > 0 {
|
||||
qs = qs.Offset((query.Page - 1) * query.Size)
|
||||
}
|
||||
}
|
||||
qs = qs.OrderBy("-CreationTime")
|
||||
|
||||
labels := []*models.Label{}
|
||||
_, err := qs.All(&labels)
|
||||
return labels, err
|
||||
}
|
||||
|
||||
func getLabelQuerySetter(query *models.LabelQuery) orm.QuerySeter {
|
||||
qs := GetOrmer().QueryTable(&models.Label{})
|
||||
if len(query.Name) > 0 {
|
||||
if query.FuzzyMatchName {
|
||||
qs = qs.Filter("Name__icontains", libOrm.Escape(query.Name))
|
||||
} else {
|
||||
qs = qs.Filter("Name", query.Name)
|
||||
}
|
||||
}
|
||||
if len(query.Level) > 0 {
|
||||
qs = qs.Filter("Level", query.Level)
|
||||
}
|
||||
if len(query.Scope) > 0 {
|
||||
qs = qs.Filter("Scope", query.Scope)
|
||||
}
|
||||
if query.ProjectID != 0 {
|
||||
qs = qs.Filter("ProjectID", query.ProjectID)
|
||||
}
|
||||
qs = qs.Filter("Deleted", false)
|
||||
return qs
|
||||
}
|
||||
|
||||
// UpdateLabel ...
|
||||
func UpdateLabel(label *models.Label) error {
|
||||
label.UpdateTime = time.Now()
|
||||
_, err := GetOrmer().Update(label)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteLabel ...
|
||||
func DeleteLabel(id int64) error {
|
||||
label, err := GetLabel(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
label.Name = fmt.Sprintf("%s#%d", label.Name, label.ID)
|
||||
label.UpdateTime = time.Now()
|
||||
label.Deleted = true
|
||||
_, err = GetOrmer().Update(label, "Name", "UpdateTime", "Deleted")
|
||||
return err
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMethodsOfLabel(t *testing.T) {
|
||||
labelName := "test"
|
||||
label := &models.Label{
|
||||
Name: labelName,
|
||||
Level: common.LabelLevelUser,
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
}
|
||||
|
||||
// add
|
||||
id, err := AddLabel(label)
|
||||
require.Nil(t, err)
|
||||
label.ID = id
|
||||
|
||||
// add a label which has the same name to another project
|
||||
projectID, err := AddProject(models.Project{
|
||||
OwnerID: 1,
|
||||
Name: "project_for_label_test",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer GetOrmer().QueryTable(&models.Project{}).
|
||||
Filter("project_id", projectID).Delete()
|
||||
|
||||
id2, err := AddLabel(&models.Label{
|
||||
Name: labelName,
|
||||
Level: common.LabelLevelUser,
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: projectID,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteLabel(id2)
|
||||
|
||||
// get
|
||||
l, err := GetLabel(id)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, label.ID, l.ID)
|
||||
assert.Equal(t, label.Name, l.Name)
|
||||
assert.Equal(t, label.Scope, l.Scope)
|
||||
assert.Equal(t, label.ProjectID, l.ProjectID)
|
||||
|
||||
// get total count
|
||||
total, err := GetTotalOfLabels(&models.LabelQuery{
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
|
||||
// list: exact match
|
||||
labels, err := ListLabels(&models.LabelQuery{
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
Name: label.Name,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(labels))
|
||||
|
||||
// list: fuzzy match
|
||||
labels, err = ListLabels(&models.LabelQuery{
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
Name: label.Name[:1],
|
||||
FuzzyMatchName: true,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(labels))
|
||||
|
||||
// list: not exist
|
||||
labels, err = ListLabels(&models.LabelQuery{
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
Name: label.Name[:1],
|
||||
})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(labels))
|
||||
|
||||
// update
|
||||
newName := "dev"
|
||||
label.Name = newName
|
||||
err = UpdateLabel(label)
|
||||
require.Nil(t, err)
|
||||
|
||||
l, err = GetLabel(id)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, newName, l.Name)
|
||||
|
||||
// delete
|
||||
err = DeleteLabel(id)
|
||||
require.Nil(t, err)
|
||||
|
||||
l, err = GetLabel(id)
|
||||
require.Nil(t, err)
|
||||
assert.True(t, l.Deleted)
|
||||
}
|
|
@ -15,10 +15,11 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
)
|
||||
|
||||
// AddResourceLabel add a label to a resource
|
||||
|
@ -60,7 +61,7 @@ func GetResourceLabel(rType string, rIDOrName interface{}, labelID int64) (*mode
|
|||
|
||||
// GetLabelsOfResource returns the label list of the resource
|
||||
// Get the labels by ResourceID if rIDOrName is int, or get the labels by ResourceName
|
||||
func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*models.Label, error) {
|
||||
func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*model.Label, error) {
|
||||
sql := `select l.id, l.name, l.description, l.color, l.scope, l.project_id, l.creation_time, l.update_time
|
||||
from harbor_resource_label rl
|
||||
join harbor_label l on rl.label_id=l.id
|
||||
|
@ -71,7 +72,7 @@ func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*models.Label,
|
|||
sql += ` rl.resource_name = ?`
|
||||
}
|
||||
|
||||
labels := []*models.Label{}
|
||||
labels := []*model.Label{}
|
||||
_, err := GetOrmer().Raw(sql, rType, rIDOrName).QueryRows(&labels)
|
||||
return labels, err
|
||||
}
|
||||
|
|
|
@ -15,22 +15,26 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/label/dao"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMethodsOfResourceLabel(t *testing.T) {
|
||||
labelID, err := AddLabel(&models.Label{
|
||||
labelDao := dao.New()
|
||||
labelID, err := labelDao.Create(orm.Context(), &model.Label{
|
||||
Name: "test_label",
|
||||
Level: common.LabelLevelUser,
|
||||
Scope: common.LabelScopeGlobal,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteLabel(labelID)
|
||||
defer labelDao.Delete(orm.Context(), labelID)
|
||||
|
||||
var resourceID int64 = 1
|
||||
resourceType := common.ResourceTypeRepository
|
||||
|
|
|
@ -25,7 +25,6 @@ func init() {
|
|||
new(Role),
|
||||
new(RepoRecord),
|
||||
new(ProjectMetadata),
|
||||
new(Label),
|
||||
new(ResourceLabel),
|
||||
new(JobLog),
|
||||
new(OIDCUser),
|
||||
|
|
|
@ -15,58 +15,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
)
|
||||
|
||||
// Label holds information used for a label
|
||||
type Label struct {
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Description string `orm:"column(description)" json:"description"`
|
||||
Color string `orm:"column(color)" json:"color"`
|
||||
Level string `orm:"column(level)" json:"-"`
|
||||
Scope string `orm:"column(scope)" json:"scope"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
Deleted bool `orm:"column(deleted)" json:"deleted"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (l *Label) TableName() string {
|
||||
return "harbor_label"
|
||||
}
|
||||
|
||||
// LabelQuery : query parameters for labels
|
||||
type LabelQuery struct {
|
||||
Name string
|
||||
FuzzyMatchName bool // the property is used to determine the query for lable name is fuzzy matching or exaxt matching
|
||||
Level string
|
||||
Scope string
|
||||
ProjectID int64
|
||||
Pagination
|
||||
}
|
||||
|
||||
// Valid ...
|
||||
func (l *Label) Valid(v *validation.Validation) {
|
||||
if len(l.Name) == 0 {
|
||||
v.SetError("name", "cannot be empty")
|
||||
}
|
||||
if len(l.Name) > 128 {
|
||||
v.SetError("name", "max length is 128")
|
||||
}
|
||||
|
||||
if l.Scope != common.LabelScopeGlobal && l.Scope != common.LabelScopeProject {
|
||||
v.SetError("scope", fmt.Sprintf("invalid: %s", l.Scope))
|
||||
} else if l.Scope == common.LabelScopeProject && l.ProjectID <= 0 {
|
||||
v.SetError("project_id", fmt.Sprintf("invalid: %d", l.ProjectID))
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceLabel records the relationship between resource and label
|
||||
type ResourceLabel struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
|
|
|
@ -17,51 +17,51 @@ package models
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/validation"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidOfLabel(t *testing.T) {
|
||||
cases := []struct {
|
||||
label *Label
|
||||
label *model.Label
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "invalid_scope",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "g",
|
||||
},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
ProjectID: -1,
|
||||
|
@ -69,7 +69,7 @@ func TestValidOfLabel(t *testing.T) {
|
|||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
label: &model.Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
ProjectID: 1,
|
||||
|
@ -79,8 +79,11 @@ func TestValidOfLabel(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, c := range cases {
|
||||
v := &validation.Validation{}
|
||||
c.label.Valid(v)
|
||||
assert.Equal(t, c.hasError, v.HasErrors())
|
||||
err := c.label.Valid()
|
||||
if c.hasError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/signature/notary/model"
|
||||
"github.com/lib/pq"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
@ -72,16 +71,6 @@ type RepositoryQuery struct {
|
|||
Sorting
|
||||
}
|
||||
|
||||
// TagResp holds the information of one image tag
|
||||
type TagResp struct {
|
||||
TagDetail
|
||||
Signature *model.Target `json:"signature"`
|
||||
ScanOverview map[string]interface{} `json:"scan_overview,omitempty"`
|
||||
Labels []*Label `json:"labels"`
|
||||
PushTime time.Time `json:"push_time"`
|
||||
PullTime time.Time `json:"pull_time"`
|
||||
}
|
||||
|
||||
// TagDetail ...
|
||||
type TagDetail struct {
|
||||
Digest string `json:"digest"`
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
model_tag "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
tagtesting "github.com/goharbor/harbor/src/testing/controller/tag"
|
||||
ormtesting "github.com/goharbor/harbor/src/testing/lib/orm"
|
||||
|
@ -64,7 +65,7 @@ type controllerTestSuite struct {
|
|||
artrashMgr *artrashtesting.FakeManager
|
||||
blobMgr *blob.Manager
|
||||
tagCtl *tagtesting.FakeController
|
||||
labelMgr *label.FakeManager
|
||||
labelMgr *label.Manager
|
||||
abstractor *fakeAbstractor
|
||||
immutableMtr *immutable.FakeMatcher
|
||||
regCli *registry.FakeClient
|
||||
|
@ -76,7 +77,7 @@ func (c *controllerTestSuite) SetupTest() {
|
|||
c.artrashMgr = &artrashtesting.FakeManager{}
|
||||
c.blobMgr = &blob.Manager{}
|
||||
c.tagCtl = &tagtesting.FakeController{}
|
||||
c.labelMgr = &label.FakeManager{}
|
||||
c.labelMgr = &label.Manager{}
|
||||
c.abstractor = &fakeAbstractor{}
|
||||
c.immutableMtr = &immutable.FakeMatcher{}
|
||||
c.regCli = ®istry.FakeClient{}
|
||||
|
@ -118,11 +119,11 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
|
|||
}
|
||||
c.tagCtl.On("List").Return([]*tag.Tag{tg}, nil)
|
||||
ctx := lib.WithAPIVersion(nil, "2.0")
|
||||
lb := &models.Label{
|
||||
lb := &model.Label{
|
||||
ID: 1,
|
||||
Name: "label",
|
||||
}
|
||||
c.labelMgr.On("ListByArtifact").Return([]*models.Label{
|
||||
c.labelMgr.On("ListByArtifact", mock.Anything, mock.Anything).Return([]*model.Label{
|
||||
lb,
|
||||
}, nil)
|
||||
artifact := c.ctl.assembleArtifact(ctx, art, option)
|
||||
|
@ -537,13 +538,13 @@ func (c *controllerTestSuite) TestGetAddition() {
|
|||
}
|
||||
|
||||
func (c *controllerTestSuite) TestAddTo() {
|
||||
c.labelMgr.On("AddTo").Return(nil)
|
||||
c.labelMgr.On("AddTo", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
err := c.ctl.AddLabel(context.Background(), 1, 1)
|
||||
c.Require().Nil(err)
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestRemoveFrom() {
|
||||
c.labelMgr.On("RemoveFrom").Return(nil)
|
||||
c.labelMgr.On("RemoveFrom", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
err := c.ctl.RemoveLabel(nil, 1, 1)
|
||||
c.Require().Nil(err)
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@ package artifact
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
cmodels "github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/controller/tag"
|
||||
"github.com/goharbor/harbor/src/lib/encode/repository"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
)
|
||||
|
||||
// Artifact is the overall view of artifact
|
||||
|
@ -28,7 +28,7 @@ type Artifact struct {
|
|||
artifact.Artifact
|
||||
Tags []*tag.Tag `json:"tags"` // the list of tags that attached to the artifact
|
||||
AdditionLinks map[string]*AdditionLink `json:"addition_links"` // the resource link for build history(image), values.yaml(chart), dependency(chart), etc
|
||||
Labels []*cmodels.Label `json:"labels"`
|
||||
Labels []*model.Label `json:"labels"`
|
||||
}
|
||||
|
||||
// SetAdditionLink set a addition link
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/lib/selector"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat"
|
||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/instance"
|
||||
pol "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy"
|
||||
|
@ -570,7 +571,7 @@ func pureRepository(ns, r string) string {
|
|||
}
|
||||
|
||||
// getLabels gets label texts from the label objects
|
||||
func getLabels(labels []*models.Label) []string {
|
||||
func getLabels(labels []*model.Label) []string {
|
||||
lt := make([]string, 0)
|
||||
for _, l := range labels {
|
||||
lt = append(lt, l.Name)
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/goharbor/harbor/src/lib/selector"
|
||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||
ar "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
po "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy"
|
||||
pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
||||
"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
|
||||
|
@ -265,7 +266,7 @@ func mockArtifacts() []*car.Artifact {
|
|||
Signed: false,
|
||||
},
|
||||
},
|
||||
Labels: []*models.Label{
|
||||
Labels: []*model.Label{
|
||||
{
|
||||
Name: "approved",
|
||||
}, {
|
||||
|
@ -293,7 +294,7 @@ func mockArtifacts() []*car.Artifact {
|
|||
Signed: true,
|
||||
},
|
||||
},
|
||||
Labels: []*models.Label{
|
||||
Labels: []*model.Label{
|
||||
{
|
||||
Name: "approved",
|
||||
}, {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -63,7 +64,7 @@ func (cla *ChartLabelAPI) MarkLabel() {
|
|||
return
|
||||
}
|
||||
|
||||
l := &models.Label{}
|
||||
l := &model.Label{}
|
||||
if err := cla.DecodeJSONReq(l); err != nil {
|
||||
cla.SendBadRequestError(err)
|
||||
return
|
||||
|
|
|
@ -16,6 +16,7 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -23,8 +24,8 @@ import (
|
|||
"github.com/goharbor/harbor/src/chartserver"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/api"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
pkg_dao "github.com/goharbor/harbor/src/pkg/label/dao"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -36,6 +37,7 @@ var (
|
|||
cProLibraryLabelID int64
|
||||
mockChartServer *httptest.Server
|
||||
oldChartController *chartserver.Controller
|
||||
labelDao pkg_dao.DAO
|
||||
)
|
||||
|
||||
func TestToStartMockChartService(t *testing.T) {
|
||||
|
@ -44,26 +46,28 @@ func TestToStartMockChartService(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to start the mock chart service: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAddToChart(t *testing.T) {
|
||||
cSysLevelLabelID, err := dao.AddLabel(&models.Label{
|
||||
labelDao = pkg_dao.New()
|
||||
cSysLevelLabelID, err := labelDao.Create(orm.Context(), &model.Label{
|
||||
Name: "c_sys_level_label",
|
||||
Level: common.LabelLevelSystem,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer dao.DeleteLabel(cSysLevelLabelID)
|
||||
defer labelDao.Delete(orm.Context(), cSysLevelLabelID)
|
||||
|
||||
cProTestLabelID, err := dao.AddLabel(&models.Label{
|
||||
cProTestLabelID, err := labelDao.Create(orm.Context(), &model.Label{
|
||||
Name: "c_pro_test_label",
|
||||
Level: common.LabelLevelUser,
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 100,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer dao.DeleteLabel(cProTestLabelID)
|
||||
defer labelDao.Delete(orm.Context(), cProTestLabelID)
|
||||
|
||||
cProLibraryLabelID, err = dao.AddLabel(&models.Label{
|
||||
cProLibraryLabelID, err = labelDao.Create(orm.Context(), &model.Label{
|
||||
Name: "c_pro_library_label",
|
||||
Level: common.LabelLevelUser,
|
||||
Scope: common.LabelScopeProject,
|
||||
|
@ -177,7 +181,7 @@ func TestAddToChart(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetOfChart(t *testing.T) {
|
||||
labels := []*models.Label{}
|
||||
labels := []*model.Label{}
|
||||
err := handleAndParse(&testingRequest{
|
||||
url: resourceLabelAPIPath,
|
||||
method: http.MethodGet,
|
||||
|
@ -198,7 +202,7 @@ func TestRemoveFromChart(t *testing.T) {
|
|||
code: http.StatusOK,
|
||||
})
|
||||
|
||||
labels := []*models.Label{}
|
||||
labels := []*model.Label{}
|
||||
err := handleAndParse(&testingRequest{
|
||||
url: resourceLabelAPIPath,
|
||||
method: http.MethodGet,
|
||||
|
@ -216,6 +220,6 @@ func TestToStopMockChartService(t *testing.T) {
|
|||
if oldChartController != nil {
|
||||
chartController = oldChartController
|
||||
}
|
||||
|
||||
dao.DeleteLabel(cProLibraryLabelID)
|
||||
labelDao = pkg_dao.New()
|
||||
labelDao.Delete(orm.Context(), cProLibraryLabelID)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/goharbor/harbor/src/controller/project"
|
||||
"github.com/goharbor/harbor/src/core/label"
|
||||
hlog "github.com/goharbor/harbor/src/lib/log"
|
||||
pkg_label "github.com/goharbor/harbor/src/pkg/label"
|
||||
n_event "github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/model"
|
||||
"github.com/goharbor/harbor/src/server/middleware/orm"
|
||||
|
@ -99,7 +100,9 @@ func (cra *ChartRepositoryAPI) Prepare() {
|
|||
}
|
||||
|
||||
// Init label manager
|
||||
cra.labelManager = &label.BaseManager{}
|
||||
cra.labelManager = &label.BaseManager{
|
||||
LabelMgr: pkg_label.Mgr,
|
||||
}
|
||||
}
|
||||
|
||||
func (cra *ChartRepositoryAPI) requireAccess(action rbac.Action, subresource ...rbac.Resource) bool {
|
||||
|
|
|
@ -98,8 +98,6 @@ func init() {
|
|||
beego.Router("/api/projects/:id([0-9]+)/metadatas/:name", &MetadataAPI{}, "put:Put;delete:Delete")
|
||||
beego.Router("/api/statistics", &StatisticAPI{})
|
||||
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
|
||||
beego.Router("/api/labels", &LabelAPI{}, "post:Post;get:List")
|
||||
beego.Router("/api/labels/:id([0-9]+", &LabelAPI{}, "get:Get;put:Put;delete:Delete")
|
||||
|
||||
// Charts are controlled under projects
|
||||
chartRepositoryAPIType := &ChartRepositoryAPI{}
|
||||
|
|
|
@ -1,310 +0,0 @@
|
|||
// Copyright 2018 Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/common/rbac/system"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/label"
|
||||
)
|
||||
|
||||
// LabelAPI handles requests for label management
|
||||
type LabelAPI struct {
|
||||
label *models.Label
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Prepare ...
|
||||
func (l *LabelAPI) Prepare() {
|
||||
l.BaseController.Prepare()
|
||||
method := l.Ctx.Request.Method
|
||||
if method == http.MethodGet {
|
||||
return
|
||||
}
|
||||
|
||||
// POST, PUT, DELETE need login first
|
||||
if !l.SecurityCtx.IsAuthenticated() {
|
||||
l.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
||||
return
|
||||
}
|
||||
|
||||
if method == http.MethodPut || method == http.MethodDelete {
|
||||
id, err := l.GetInt64FromPath(":id")
|
||||
if err != nil || id <= 0 {
|
||||
l.SendBadRequestError(errors.New("invalid label ID"))
|
||||
return
|
||||
}
|
||||
|
||||
label, err := dao.GetLabel(id)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
|
||||
if label == nil || label.Deleted {
|
||||
l.SendNotFoundError(fmt.Errorf("label %d not found", id))
|
||||
return
|
||||
}
|
||||
|
||||
l.label = label
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LabelAPI) requireAccess(label *models.Label, action rbac.Action, subresources ...rbac.Resource) bool {
|
||||
var hasPermission bool
|
||||
|
||||
switch label.Scope {
|
||||
case common.LabelScopeGlobal:
|
||||
resource := system.NewNamespace().Resource(rbac.ResourceLabel)
|
||||
hasPermission = l.SecurityCtx.Can(l.Context(), action, resource)
|
||||
case common.LabelScopeProject:
|
||||
if len(subresources) == 0 {
|
||||
subresources = append(subresources, rbac.ResourceLabel)
|
||||
}
|
||||
hasPermission, _ = l.HasProjectPermission(label.ProjectID, action, subresources...)
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
if !l.SecurityCtx.IsAuthenticated() {
|
||||
l.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
||||
} else {
|
||||
l.SendForbiddenError(errors.New(l.SecurityCtx.GetUsername()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Post creates a label
|
||||
func (l *LabelAPI) Post() {
|
||||
label := &models.Label{}
|
||||
isValid, err := l.DecodeJSONReqAndValidate(label)
|
||||
if !isValid {
|
||||
l.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
|
||||
label.Level = common.LabelLevelUser
|
||||
|
||||
switch label.Scope {
|
||||
case common.LabelScopeGlobal:
|
||||
label.ProjectID = 0
|
||||
case common.LabelScopeProject:
|
||||
exist, err := l.ProjectCtl.Exists(l.Context(), label.ProjectID)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to check the existence of project %d: %v",
|
||||
label.ProjectID, err))
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
l.SendBadRequestError(fmt.Errorf("project %d not found", label.ProjectID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !l.requireAccess(label, rbac.ActionCreate) {
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := dao.ListLabels(&models.LabelQuery{
|
||||
Name: label.Name,
|
||||
Level: label.Level,
|
||||
Scope: label.Scope,
|
||||
ProjectID: label.ProjectID,
|
||||
})
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
|
||||
return
|
||||
}
|
||||
if len(labels) > 0 {
|
||||
l.SendConflictError(errors.New("conflict label"))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := dao.AddLabel(label)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to create label: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
l.Redirect(http.StatusCreated, strconv.FormatInt(id, 10))
|
||||
}
|
||||
|
||||
// Get the label specified by ID
|
||||
func (l *LabelAPI) Get() {
|
||||
id, err := l.GetInt64FromPath(":id")
|
||||
if err != nil || id <= 0 {
|
||||
l.SendBadRequestError(fmt.Errorf("invalid label ID: %s", l.GetStringFromPath(":id")))
|
||||
return
|
||||
}
|
||||
|
||||
label, err := dao.GetLabel(id)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to get label %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
|
||||
if label == nil || label.Deleted {
|
||||
l.SendNotFoundError(fmt.Errorf("label %d not found", id))
|
||||
return
|
||||
}
|
||||
|
||||
if !l.requireAccess(label, rbac.ActionRead) {
|
||||
return
|
||||
}
|
||||
|
||||
l.Data["json"] = label
|
||||
l.ServeJSON()
|
||||
}
|
||||
|
||||
// List labels according to the query strings
|
||||
func (l *LabelAPI) List() {
|
||||
query := &models.LabelQuery{
|
||||
Name: l.GetString("name"),
|
||||
FuzzyMatchName: true,
|
||||
Level: common.LabelLevelUser,
|
||||
}
|
||||
|
||||
scope := l.GetString("scope")
|
||||
if scope != common.LabelScopeGlobal && scope != common.LabelScopeProject {
|
||||
l.SendBadRequestError(fmt.Errorf("invalid scope: %s", scope))
|
||||
return
|
||||
}
|
||||
query.Scope = scope
|
||||
|
||||
if scope == common.LabelScopeProject {
|
||||
projectIDStr := l.GetString("project_id")
|
||||
if len(projectIDStr) == 0 {
|
||||
l.SendBadRequestError(errors.New("project_id is required"))
|
||||
return
|
||||
}
|
||||
projectID, err := strconv.ParseInt(projectIDStr, 10, 64)
|
||||
if err != nil || projectID <= 0 {
|
||||
l.SendBadRequestError(fmt.Errorf("invalid project_id: %s", projectIDStr))
|
||||
return
|
||||
}
|
||||
|
||||
if !l.RequireProjectAccess(projectID, rbac.ActionList, rbac.ResourceLabel) {
|
||||
return
|
||||
}
|
||||
query.ProjectID = projectID
|
||||
}
|
||||
|
||||
total, err := dao.GetTotalOfLabels(query)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to get total count of labels: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
query.Page, query.Size, err = l.GetPaginationParams()
|
||||
if err != nil {
|
||||
l.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := dao.ListLabels(query)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
l.SetPaginationHeader(total, query.Page, query.Size)
|
||||
l.Data["json"] = labels
|
||||
l.ServeJSON()
|
||||
}
|
||||
|
||||
// Put updates the label
|
||||
func (l *LabelAPI) Put() {
|
||||
if !l.requireAccess(l.label, rbac.ActionUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
label := &models.Label{}
|
||||
if err := l.DecodeJSONReq(label); err != nil {
|
||||
l.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
|
||||
oldName := l.label.Name
|
||||
|
||||
// only name, description and color can be changed
|
||||
l.label.Name = label.Name
|
||||
l.label.Description = label.Description
|
||||
l.label.Color = label.Color
|
||||
|
||||
isValidate, err := l.Validate(l.label)
|
||||
if !isValidate {
|
||||
if err != nil {
|
||||
l.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.label.Name != oldName {
|
||||
labels, err := dao.ListLabels(&models.LabelQuery{
|
||||
Name: l.label.Name,
|
||||
Level: l.label.Level,
|
||||
Scope: l.label.Scope,
|
||||
ProjectID: l.label.ProjectID,
|
||||
})
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to list labels: %v", err))
|
||||
return
|
||||
}
|
||||
if len(labels) > 0 {
|
||||
l.SendConflictError(errors.New("conflict label"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := dao.UpdateLabel(l.label); err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to update label %d: %v", l.label.ID, err))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Delete the label
|
||||
func (l *LabelAPI) Delete() {
|
||||
if !l.requireAccess(l.label, rbac.ActionDelete) {
|
||||
return
|
||||
}
|
||||
|
||||
id := l.label.ID
|
||||
if err := dao.DeleteResourceLabelByLabel(id); err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to delete resource label mappings of label %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := label.Mgr.RemoveFromAllArtifacts(orm.Context(), id); err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to remove the label %d from all artifacts: %v", id, err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := dao.DeleteLabel(id); err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf("failed to delete label %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
pkg_label "github.com/goharbor/harbor/src/pkg/label"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/core/label"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
)
|
||||
|
||||
// LabelResourceAPI provides the related basic functions to handle marking labels to resources
|
||||
|
@ -19,7 +21,9 @@ func (lra *LabelResourceAPI) Prepare() {
|
|||
lra.BaseController.Prepare()
|
||||
|
||||
// Create label manager
|
||||
lra.labelManager = &label.BaseManager{}
|
||||
lra.labelManager = &label.BaseManager{
|
||||
LabelMgr: pkg_label.Mgr,
|
||||
}
|
||||
}
|
||||
|
||||
func (lra *LabelResourceAPI) getLabelsOfResource(rType string, rIDOrName interface{}) {
|
||||
|
@ -52,7 +56,7 @@ func (lra *LabelResourceAPI) removeLabelFromResource(rType string, rIDOrName int
|
|||
}
|
||||
|
||||
// eat the error of validate method of label manager
|
||||
func (lra *LabelResourceAPI) validate(labelID, projectID int64) (*models.Label, bool) {
|
||||
func (lra *LabelResourceAPI) validate(labelID, projectID int64) (*model.Label, bool) {
|
||||
label, err := lra.labelManager.Validate(labelID, projectID)
|
||||
if err != nil {
|
||||
lra.handleErrors(err)
|
||||
|
@ -63,7 +67,7 @@ func (lra *LabelResourceAPI) validate(labelID, projectID int64) (*models.Label,
|
|||
}
|
||||
|
||||
// eat the error of exists method of label manager
|
||||
func (lra *LabelResourceAPI) exists(labelID int64) (*models.Label, bool) {
|
||||
func (lra *LabelResourceAPI) exists(labelID int64) (*model.Label, bool) {
|
||||
label, err := lra.labelManager.Exists(labelID)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
|
|
|
@ -1,435 +0,0 @@
|
|||
// Copyright 2018 Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
labelAPIBasePath = "/api/labels"
|
||||
labelID int64
|
||||
)
|
||||
|
||||
func TestLabelAPIPost(t *testing.T) {
|
||||
postFunc := func(resp *httptest.ResponseRecorder) error {
|
||||
id, err := parseResourceID(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
labelID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
|
||||
// 400
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{},
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 403 non-sysadmin try to create global label
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeGlobal,
|
||||
},
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 403 non-member user try to create project label
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 403 developer try to create project label
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: projDeveloper,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 400 non-exist project
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 10000,
|
||||
},
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusCreated,
|
||||
postFunc: postFunc,
|
||||
},
|
||||
|
||||
// 409
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: labelAPIBasePath,
|
||||
bodyJSON: &models.Label{
|
||||
Name: "test",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusConflict,
|
||||
},
|
||||
}
|
||||
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLabelAPIGet(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 400
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 0),
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 404
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 1000),
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestLabelAPIList(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 400 no scope query string
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: labelAPIBasePath,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 400 invalid scope
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: labelAPIBasePath,
|
||||
queryStruct: struct {
|
||||
Scope string `url:"scope"`
|
||||
}{
|
||||
Scope: "invalid_scope",
|
||||
},
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 400 invalid project_id
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: labelAPIBasePath,
|
||||
queryStruct: struct {
|
||||
Scope string `url:"scope"`
|
||||
ProjectID int64 `url:"project_id"`
|
||||
}{
|
||||
Scope: "p",
|
||||
ProjectID: 0,
|
||||
},
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
|
||||
// 200
|
||||
labels := []*models.Label{}
|
||||
err := handleAndParse(&testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: labelAPIBasePath,
|
||||
queryStruct: struct {
|
||||
Scope string `url:"scope"`
|
||||
ProjectID int64 `url:"project_id"`
|
||||
Name string `url:"name"`
|
||||
}{
|
||||
Scope: "p",
|
||||
ProjectID: 1,
|
||||
Name: "tes",
|
||||
},
|
||||
}, &labels)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(labels))
|
||||
|
||||
err = handleAndParse(&testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: labelAPIBasePath,
|
||||
queryStruct: struct {
|
||||
Scope string `url:"scope"`
|
||||
ProjectID int64 `url:"project_id"`
|
||||
Name string `url:"name"`
|
||||
}{
|
||||
Scope: "p",
|
||||
ProjectID: 1,
|
||||
Name: "dev",
|
||||
},
|
||||
}, &labels)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(labels))
|
||||
}
|
||||
|
||||
func TestLabelAPIPut(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
|
||||
// 400
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 0),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 404
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 10000),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
|
||||
// 403 non-member user
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 403 developer
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: projDeveloper,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 400
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
bodyJSON: &models.Label{
|
||||
Name: "",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
bodyJSON: &models.Label{
|
||||
Name: "product",
|
||||
Scope: common.LabelScopeProject,
|
||||
ProjectID: 1,
|
||||
},
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
runCodeCheckingCases(t, cases...)
|
||||
|
||||
label := &models.Label{}
|
||||
err := handleAndParse(&testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
}, label)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "product", label.Name)
|
||||
}
|
||||
|
||||
func TestLabelAPIDelete(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
|
||||
// 400
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 0),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// 404
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, 10000),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
|
||||
// 403 non-member user
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 403 developer
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: projDeveloper,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
|
||||
// 404
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/%d", labelAPIBasePath, labelID),
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/label"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
)
|
||||
|
||||
// Manager defines the related operations for label management
|
||||
|
@ -28,24 +31,26 @@ type Manager interface {
|
|||
//
|
||||
// If succeed, a label list is returned.
|
||||
// Otherwise, a non-nil error will be returned.
|
||||
GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*models.Label, error)
|
||||
GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*model.Label, error)
|
||||
|
||||
// Check the existence of the specified label.
|
||||
//
|
||||
// If label existing, a non-nil label object is returned and nil error is set.
|
||||
// A non-nil error will be set if any issues met while checking or label is not found.
|
||||
Exists(labelID int64) (*models.Label, error)
|
||||
Exists(labelID int64) (*model.Label, error)
|
||||
|
||||
// Validate if the scope of the input label is correct.
|
||||
// If the scope is project level, the projectID is required then.
|
||||
//
|
||||
// If everything is ok, an validated label reference will be returned.
|
||||
// Otherwise, a non-nil error is returned.
|
||||
Validate(labelID int64, projectID int64) (*models.Label, error)
|
||||
Validate(labelID int64, projectID int64) (*model.Label, error)
|
||||
}
|
||||
|
||||
// BaseManager is the default implementation of the Manager interface.
|
||||
type BaseManager struct{}
|
||||
type BaseManager struct {
|
||||
LabelMgr label.Manager
|
||||
}
|
||||
|
||||
// MarkLabelToResource is the implementation of same method in Manager interface.
|
||||
func (bm *BaseManager) MarkLabelToResource(label *models.ResourceLabel) (int64, error) {
|
||||
|
@ -97,7 +102,7 @@ func (bm *BaseManager) RemoveLabelFromResource(resourceType string, resourceIDOr
|
|||
}
|
||||
|
||||
// GetLabelsOfResource is the implementation of same method in Manager interface.
|
||||
func (bm *BaseManager) GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*models.Label, error) {
|
||||
func (bm *BaseManager) GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*model.Label, error) {
|
||||
labels, err := dao.GetLabelsOfResource(resourceType, resourceIDOrName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get labels of resource %s %v: %v", resourceType, resourceIDOrName, err)
|
||||
|
@ -107,30 +112,28 @@ func (bm *BaseManager) GetLabelsOfResource(resourceType string, resourceIDOrName
|
|||
}
|
||||
|
||||
// Exists is the implementation of same method in Manager interface.
|
||||
func (bm *BaseManager) Exists(labelID int64) (*models.Label, error) {
|
||||
label, err := dao.GetLabel(labelID)
|
||||
func (bm *BaseManager) Exists(labelID int64) (*model.Label, error) {
|
||||
label, err := bm.LabelMgr.Get(orm.Context(), labelID)
|
||||
if err != nil {
|
||||
if errors.IsErr(err, errors.NotFoundCode) {
|
||||
return nil, NewErrLabelNotFound(labelID, "", nil)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get label %d: %v", labelID, err)
|
||||
}
|
||||
|
||||
if label == nil {
|
||||
return nil, NewErrLabelNotFound(labelID, "", nil)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
||||
|
||||
// Validate is the implementation of same method in Manager interface.
|
||||
func (bm *BaseManager) Validate(labelID int64, projectID int64) (*models.Label, error) {
|
||||
label, err := dao.GetLabel(labelID)
|
||||
func (bm *BaseManager) Validate(labelID int64, projectID int64) (*model.Label, error) {
|
||||
label, err := bm.LabelMgr.Get(orm.Context(), labelID)
|
||||
if err != nil {
|
||||
if errors.IsErr(err, errors.NotFoundCode) {
|
||||
return nil, NewErrLabelNotFound(labelID, "", nil)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get label %d: %v", labelID, err)
|
||||
}
|
||||
|
||||
if label == nil {
|
||||
return nil, NewErrLabelNotFound(labelID, "", nil)
|
||||
}
|
||||
|
||||
if label.Level != common.LabelLevelUser {
|
||||
return nil, NewErrLabelBadRequest("only user level labels can be used")
|
||||
}
|
||||
|
|
|
@ -12,52 +12,55 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package label
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
beego_orm "github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
beego_orm.RegisterModel(&Reference{})
|
||||
}
|
||||
|
||||
// DAO is the data access object interface for label
|
||||
type DAO interface {
|
||||
// Get the specified label
|
||||
Get(ctx context.Context, id int64) (label *models.Label, err error)
|
||||
Get(ctx context.Context, id int64) (label *model.Label, err error)
|
||||
// Create the label
|
||||
Create(ctx context.Context, label *models.Label) (id int64, err error)
|
||||
Create(ctx context.Context, label *model.Label) (id int64, err error)
|
||||
// Count returns the total count of Labels according to the query
|
||||
Count(ctx context.Context, query *q.Query) (total int64, err error)
|
||||
// Update the label
|
||||
Update(ctx context.Context, label *model.Label) error
|
||||
// Delete the label
|
||||
Delete(ctx context.Context, id int64) (err error)
|
||||
// List ...
|
||||
List(ctx context.Context, query *q.Query) ([]*model.Label, error)
|
||||
|
||||
// List labels that added to the artifact specified by the ID
|
||||
ListByArtifact(ctx context.Context, artifactID int64) (labels []*models.Label, err error)
|
||||
ListByArtifact(ctx context.Context, artifactID int64) (labels []*model.Label, err error)
|
||||
// Create label reference
|
||||
CreateReference(ctx context.Context, reference *Reference) (id int64, err error)
|
||||
CreateReference(ctx context.Context, reference *model.Reference) (id int64, err error)
|
||||
// Delete the label reference specified by ID
|
||||
DeleteReference(ctx context.Context, id int64) (err error)
|
||||
// Delete label references specified by query
|
||||
DeleteReferences(ctx context.Context, query *q.Query) (n int64, err error)
|
||||
}
|
||||
|
||||
// NewDAO creates an instance of the default DAO
|
||||
func NewDAO() DAO {
|
||||
// New creates an instance of the default DAO
|
||||
func New() DAO {
|
||||
return &defaultDAO{}
|
||||
}
|
||||
|
||||
type defaultDAO struct{}
|
||||
|
||||
func (d *defaultDAO) Get(ctx context.Context, id int64) (*models.Label, error) {
|
||||
func (d *defaultDAO) Get(ctx context.Context, id int64) (*model.Label, error) {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
label := &models.Label{
|
||||
label := &model.Label{
|
||||
ID: id,
|
||||
}
|
||||
if err = ormer.Read(label); err != nil {
|
||||
|
@ -69,7 +72,7 @@ func (d *defaultDAO) Get(ctx context.Context, id int64) (*models.Label, error) {
|
|||
return label, nil
|
||||
}
|
||||
|
||||
func (d *defaultDAO) Create(ctx context.Context, label *models.Label) (int64, error) {
|
||||
func (d *defaultDAO) Create(ctx context.Context, label *model.Label) (int64, error) {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -83,12 +86,36 @@ func (d *defaultDAO) Create(ctx context.Context, label *models.Label) (int64, er
|
|||
return id, err
|
||||
}
|
||||
|
||||
func (d *defaultDAO) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
qs, err := orm.QuerySetterForCount(ctx, &model.Label{}, query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return qs.Count()
|
||||
}
|
||||
|
||||
func (d *defaultDAO) Update(ctx context.Context, label *model.Label) error {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
label.UpdateTime = time.Now()
|
||||
n, err := ormer.Update(label)
|
||||
if n == 0 {
|
||||
if e := orm.AsConflictError(err, "label %s already exists", label.Name); e != nil {
|
||||
err = e
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *defaultDAO) Delete(ctx context.Context, id int64) error {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := ormer.Delete(&models.Label{
|
||||
n, err := ormer.Delete(&model.Label{
|
||||
ID: id,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -100,7 +127,19 @@ func (d *defaultDAO) Delete(ctx context.Context, id int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *defaultDAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*models.Label, error) {
|
||||
func (d *defaultDAO) List(ctx context.Context, query *q.Query) ([]*model.Label, error) {
|
||||
robots := []*model.Label{}
|
||||
qs, err := orm.QuerySetter(ctx, &model.Label{}, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = qs.All(&robots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return robots, nil
|
||||
}
|
||||
|
||||
func (d *defaultDAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) {
|
||||
sql := `select label.* from harbor_label label
|
||||
join label_reference ref on label.id = ref.label_id
|
||||
where ref.artifact_id = ?`
|
||||
|
@ -108,13 +147,13 @@ func (d *defaultDAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*m
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
labels := []*models.Label{}
|
||||
labels := []*model.Label{}
|
||||
if _, err = ormer.Raw(sql, artifactID).QueryRows(&labels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
func (d *defaultDAO) CreateReference(ctx context.Context, ref *Reference) (int64, error) {
|
||||
func (d *defaultDAO) CreateReference(ctx context.Context, ref *model.Reference) (int64, error) {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -137,7 +176,7 @@ func (d *defaultDAO) DeleteReference(ctx context.Context, id int64) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := ormer.Delete(&Reference{
|
||||
n, err := ormer.Delete(&model.Reference{
|
||||
ID: id,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -150,7 +189,7 @@ func (d *defaultDAO) DeleteReference(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
func (d *defaultDAO) DeleteReferences(ctx context.Context, query *q.Query) (int64, error) {
|
||||
qs, err := orm.QuerySetter(ctx, &Reference{}, query)
|
||||
qs, err := orm.QuerySetter(ctx, &model.Reference{}, query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
|
@ -12,17 +12,17 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package label
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
beegoorm "github.com/astaxie/beego/orm"
|
||||
common_dao "github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
artdao "github.com/goharbor/harbor/src/pkg/artifact/dao"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
|
@ -46,7 +46,7 @@ func (l *labelDaoTestSuite) SetupSuite() {
|
|||
}
|
||||
|
||||
func (l *labelDaoTestSuite) SetupTest() {
|
||||
id, err := l.dao.Create(l.ctx, &models.Label{
|
||||
id, err := l.dao.Create(l.ctx, &model.Label{
|
||||
Name: "label_for_label_dao_test_suite",
|
||||
Scope: "g",
|
||||
})
|
||||
|
@ -64,7 +64,7 @@ func (l *labelDaoTestSuite) SetupTest() {
|
|||
l.Require().Nil(err)
|
||||
l.artID = id
|
||||
|
||||
id, err = l.dao.CreateReference(l.ctx, &Reference{
|
||||
id, err = l.dao.CreateReference(l.ctx, &model.Reference{
|
||||
LabelID: l.id,
|
||||
ArtifactID: l.artID,
|
||||
})
|
||||
|
@ -99,7 +99,7 @@ func (l *labelDaoTestSuite) TestCreate() {
|
|||
// happy pass is covered by SetupTest
|
||||
|
||||
// conflict
|
||||
_, err := l.dao.Create(l.ctx, &models.Label{
|
||||
_, err := l.dao.Create(l.ctx, &model.Label{
|
||||
Name: "label_for_label_dao_test_suite",
|
||||
Scope: "g",
|
||||
})
|
||||
|
@ -127,7 +127,7 @@ func (l *labelDaoTestSuite) TestCreateReference() {
|
|||
// happy pass is covered by SetupTest
|
||||
|
||||
// conflict
|
||||
_, err := l.dao.CreateReference(l.ctx, &Reference{
|
||||
_, err := l.dao.CreateReference(l.ctx, &model.Reference{
|
||||
LabelID: l.id,
|
||||
ArtifactID: l.artID,
|
||||
})
|
||||
|
@ -135,7 +135,7 @@ func (l *labelDaoTestSuite) TestCreateReference() {
|
|||
l.True(errors.IsErr(err, errors.ConflictCode))
|
||||
|
||||
// violating foreign key constraint: the label that the ref tries to refer doesn't exist
|
||||
_, err = l.dao.CreateReference(l.ctx, &Reference{
|
||||
_, err = l.dao.CreateReference(l.ctx, &model.Reference{
|
||||
LabelID: 1000,
|
||||
ArtifactID: l.artID,
|
||||
})
|
||||
|
@ -143,7 +143,7 @@ func (l *labelDaoTestSuite) TestCreateReference() {
|
|||
l.True(errors.IsErr(err, errors.NotFoundCode))
|
||||
|
||||
// violating foreign key constraint: the artifact that the ref tries to refer doesn't exist
|
||||
_, err = l.dao.CreateReference(l.ctx, &Reference{
|
||||
_, err = l.dao.CreateReference(l.ctx, &model.Reference{
|
||||
LabelID: l.id,
|
||||
ArtifactID: 1000,
|
||||
})
|
|
@ -16,9 +16,10 @@ package label
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/label/dao"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -27,10 +28,21 @@ var Mgr = New()
|
|||
|
||||
// Manager manages the labels and references between label and resource
|
||||
type Manager interface {
|
||||
// Create the label
|
||||
Create(ctx context.Context, label *model.Label) (id int64, err error)
|
||||
// Get the label specified by ID
|
||||
Get(ctx context.Context, id int64) (label *models.Label, err error)
|
||||
Get(ctx context.Context, id int64) (label *model.Label, err error)
|
||||
// Count returns the total count of Labels according to the query
|
||||
Count(ctx context.Context, query *q.Query) (total int64, err error)
|
||||
// Update the label
|
||||
Update(ctx context.Context, label *model.Label) error
|
||||
// Delete the label
|
||||
Delete(ctx context.Context, id int64) (err error)
|
||||
// List ...
|
||||
List(ctx context.Context, query *q.Query) ([]*model.Label, error)
|
||||
|
||||
// List labels that added to the artifact specified by the ID
|
||||
ListByArtifact(ctx context.Context, artifactID int64) (labels []*models.Label, err error)
|
||||
ListByArtifact(ctx context.Context, artifactID int64) (labels []*model.Label, err error)
|
||||
// Add label to the artifact specified the ID
|
||||
AddTo(ctx context.Context, labelID int64, artifactID int64) (err error)
|
||||
// Remove the label added to the artifact specified by the ID
|
||||
|
@ -44,25 +56,45 @@ type Manager interface {
|
|||
// New creates an instance of the default label manager
|
||||
func New() Manager {
|
||||
return &manager{
|
||||
dao: &defaultDAO{},
|
||||
dao: dao.New(),
|
||||
}
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
dao DAO
|
||||
dao dao.DAO
|
||||
}
|
||||
|
||||
func (m *manager) Get(ctx context.Context, id int64) (*models.Label, error) {
|
||||
func (m *manager) Create(ctx context.Context, label *model.Label) (id int64, err error) {
|
||||
return m.dao.Create(ctx, label)
|
||||
}
|
||||
|
||||
func (m *manager) Get(ctx context.Context, id int64) (*model.Label, error) {
|
||||
return m.dao.Get(ctx, id)
|
||||
}
|
||||
|
||||
func (m *manager) ListByArtifact(ctx context.Context, artifactID int64) ([]*models.Label, error) {
|
||||
func (m *manager) Count(ctx context.Context, query *q.Query) (total int64, err error) {
|
||||
return m.dao.Count(ctx, query)
|
||||
}
|
||||
|
||||
func (m *manager) Update(ctx context.Context, label *model.Label) error {
|
||||
return m.dao.Update(ctx, label)
|
||||
}
|
||||
|
||||
func (m *manager) Delete(ctx context.Context, id int64) error {
|
||||
return m.dao.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (m *manager) List(ctx context.Context, query *q.Query) ([]*model.Label, error) {
|
||||
return m.dao.List(ctx, query)
|
||||
}
|
||||
|
||||
func (m *manager) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) {
|
||||
return m.dao.ListByArtifact(ctx, artifactID)
|
||||
}
|
||||
|
||||
func (m *manager) AddTo(ctx context.Context, labelID int64, artifactID int64) error {
|
||||
now := time.Now()
|
||||
_, err := m.dao.CreateReference(ctx, &Reference{
|
||||
_, err := m.dao.CreateReference(ctx, &model.Reference{
|
||||
LabelID: labelID,
|
||||
ArtifactID: artifactID,
|
||||
CreationTime: now,
|
||||
|
|
|
@ -16,112 +16,118 @@ package label
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/label/dao"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fakeDao struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *fakeDao) Get(ctx context.Context, id int64) (*models.Label, error) {
|
||||
args := f.Called()
|
||||
var label *models.Label
|
||||
if args.Get(0) != nil {
|
||||
label = args.Get(0).(*models.Label)
|
||||
}
|
||||
return label, args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) Create(ctx context.Context, label *models.Label) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) Delete(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
func (f *fakeDao) ListByArtifact(ctx context.Context, artifactID int64) ([]*models.Label, error) {
|
||||
args := f.Called()
|
||||
var labels []*models.Label
|
||||
if args.Get(0) != nil {
|
||||
labels = args.Get(0).([]*models.Label)
|
||||
}
|
||||
return labels, args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) CreateReference(ctx context.Context, reference *Reference) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
func (f *fakeDao) DeleteReference(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
func (f *fakeDao) DeleteReferences(ctx context.Context, query *q.Query) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
|
||||
type managerTestSuite struct {
|
||||
suite.Suite
|
||||
mgr *manager
|
||||
dao *fakeDao
|
||||
dao *dao.DAO
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) SetupTest() {
|
||||
m.dao = &fakeDao{}
|
||||
m.dao = &dao.DAO{}
|
||||
m.mgr = &manager{
|
||||
dao: m.dao,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestGet() {
|
||||
m.dao.On("Get").Return(nil, nil)
|
||||
_, err := m.mgr.Get(nil, 1)
|
||||
m.Require().Nil(err)
|
||||
func (m *managerTestSuite) TestCreate() {
|
||||
m.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
_, err := m.mgr.Create(context.Background(), &model.Label{})
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestListArtifact() {
|
||||
m.dao.On("ListByArtifact").Return(nil, nil)
|
||||
_, err := m.mgr.ListByArtifact(nil, 1)
|
||||
m.Require().Nil(err)
|
||||
func (m *managerTestSuite) TestCount() {
|
||||
m.dao.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
n, err := m.mgr.Count(context.Background(), nil)
|
||||
m.Nil(err)
|
||||
m.Equal(int64(1), n)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestDelete() {
|
||||
m.dao.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
err := m.mgr.Delete(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestGet() {
|
||||
m.dao.On("Get", mock.Anything, mock.Anything).Return(&model.Label{
|
||||
ID: 1,
|
||||
Name: "label",
|
||||
}, nil)
|
||||
label, err := m.mgr.Get(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.Equal("label", label.Name)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestUpdate() {
|
||||
m.dao.On("Update", mock.Anything, mock.Anything).Return(nil)
|
||||
err := m.mgr.Update(context.Background(), &model.Label{})
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestListByArtifact() {
|
||||
m.dao.On("ListByArtifact", mock.Anything, mock.Anything).Return([]*model.Label{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "label",
|
||||
},
|
||||
}, nil)
|
||||
rpers, err := m.mgr.ListByArtifact(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.Equal(1, len(rpers))
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestList() {
|
||||
m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Label{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "label",
|
||||
},
|
||||
}, nil)
|
||||
rpers, err := m.mgr.List(context.Background(), nil)
|
||||
m.Nil(err)
|
||||
m.Equal(1, len(rpers))
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestAddTo() {
|
||||
m.dao.On("CreateReference").Return(1, nil)
|
||||
err := m.mgr.AddTo(nil, 1, 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("CreateReference", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
err := m.mgr.AddTo(context.Background(), 1, 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestRemoveFrom() {
|
||||
// success
|
||||
m.dao.On("DeleteReferences").Return(1, nil)
|
||||
err := m.mgr.RemoveFrom(nil, 1, 1)
|
||||
m.Require().Nil(err)
|
||||
|
||||
// reset mock
|
||||
m.SetupTest()
|
||||
|
||||
// not found
|
||||
m.dao.On("DeleteReferences").Return(0, nil)
|
||||
err = m.mgr.RemoveFrom(nil, 1, 1)
|
||||
m.Require().NotNil(err)
|
||||
m.True(errors.IsErr(err, errors.NotFoundCode))
|
||||
m.dao.On("DeleteReferences", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
err := m.mgr.RemoveFrom(context.Background(), 1, 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestRemoveAllFrom() {
|
||||
m.dao.On("DeleteReferences").Return(2, nil)
|
||||
err := m.mgr.RemoveAllFrom(nil, 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("DeleteReferences", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
err := m.mgr.RemoveAllFrom(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestRemoveFromAllArtifacts() {
|
||||
m.dao.On("DeleteReferences").Return(2, nil)
|
||||
err := m.mgr.RemoveFromAllArtifacts(nil, 1)
|
||||
m.Require().Nil(err)
|
||||
m.dao.On("DeleteReferences", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
err := m.mgr.RemoveFromAllArtifacts(context.Background(), 1)
|
||||
m.Nil(err)
|
||||
m.dao.AssertExpectations(m.T())
|
||||
}
|
||||
|
||||
func TestManager(t *testing.T) {
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package label
|
||||
|
||||
import "time"
|
||||
|
||||
// Reference is the reference of label and artifact
|
||||
type Reference struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
LabelID int64 `orm:"column(label_id)"`
|
||||
ArtifactID int64 `orm:"column(artifact_id)"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now"`
|
||||
}
|
||||
|
||||
// TableName defines the database table name
|
||||
func (r *Reference) TableName() string {
|
||||
return "label_reference"
|
||||
}
|
80
src/pkg/label/model/model.go
Normal file
80
src/pkg/label/model/model.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
orm.RegisterModel(&Label{})
|
||||
orm.RegisterModel(&Reference{})
|
||||
}
|
||||
|
||||
// Label holds information used for a label
|
||||
type Label struct {
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Description string `orm:"column(description)" json:"description"`
|
||||
Color string `orm:"column(color)" json:"color"`
|
||||
Level string `orm:"column(level)" json:"-"`
|
||||
Scope string `orm:"column(scope)" json:"scope"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
Deleted bool `orm:"column(deleted)" json:"deleted"`
|
||||
}
|
||||
|
||||
// Valid ...
|
||||
func (l *Label) Valid() error {
|
||||
if len(l.Name) == 0 {
|
||||
return errors.New("cannot be empty").WithCode(errors.BadRequestCode)
|
||||
}
|
||||
if len(l.Name) > 128 {
|
||||
return errors.New("max length is 128").WithCode(errors.BadRequestCode)
|
||||
}
|
||||
|
||||
if l.Scope != common.LabelScopeGlobal && l.Scope != common.LabelScopeProject {
|
||||
return errors.New(nil).WithMessage("invalid: %s", l.Scope).WithCode(errors.BadRequestCode)
|
||||
} else if l.Scope == common.LabelScopeProject && l.ProjectID <= 0 {
|
||||
return errors.New(nil).WithMessage("invalid: %d", l.ProjectID).WithCode(errors.BadRequestCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (l *Label) TableName() string {
|
||||
return "harbor_label"
|
||||
}
|
||||
|
||||
// Reference is the reference of label and artifact
|
||||
type Reference struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
LabelID int64 `orm:"column(label_id)"`
|
||||
ArtifactID int64 `orm:"column(artifact_id)"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now"`
|
||||
}
|
||||
|
||||
// TableName defines the database table name
|
||||
func (r *Reference) TableName() string {
|
||||
return "label_reference"
|
||||
}
|
||||
|
||||
// ResourceLabel records the relationship between resource and label
|
||||
type ResourceLabel struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
LabelID int64 `orm:"column(label_id)"`
|
||||
ResourceID int64 `orm:"column(resource_id)"`
|
||||
ResourceName string `orm:"column(resource_name)"`
|
||||
ResourceType string `orm:"column(resource_type)"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (r *ResourceLabel) TableName() string {
|
||||
return "harbor_resource_label"
|
||||
}
|
87
src/pkg/label/model/model_test.go
Normal file
87
src/pkg/label/model/model_test.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidOfLabel(t *testing.T) {
|
||||
cases := []struct {
|
||||
label *Label
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
label: &Label{
|
||||
Name: "",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "invalid_scope",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "g",
|
||||
},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
ProjectID: -1,
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
label: &Label{
|
||||
Name: "test",
|
||||
Scope: "p",
|
||||
ProjectID: 1,
|
||||
},
|
||||
hasError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if c.hasError {
|
||||
assert.NotNil(t, c.label.Valid())
|
||||
} else {
|
||||
assert.Nil(t, c.label.Valid())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ func New() http.Handler {
|
|||
SysteminfoAPI: newSystemInfoAPI(),
|
||||
PingAPI: newPingAPI(),
|
||||
LdapAPI: newLdapAPI(),
|
||||
LabelAPI: newLabelAPI(),
|
||||
GCAPI: newGCAPI(),
|
||||
QuotaAPI: newQuotaAPI(),
|
||||
RetentionAPI: newRetentionAPI(),
|
||||
|
|
189
src/server/v2.0/handler/label.go
Normal file
189
src/server/v2.0/handler/label.go
Normal file
|
@ -0,0 +1,189 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/rbac/system"
|
||||
"github.com/goharbor/harbor/src/controller/project"
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/label"
|
||||
pkg_model "github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/label"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newLabelAPI() *labelAPI {
|
||||
return &labelAPI{
|
||||
labelMgr: label.Mgr,
|
||||
projectCtl: project.Ctl,
|
||||
}
|
||||
}
|
||||
|
||||
type labelAPI struct {
|
||||
BaseAPI
|
||||
labelMgr label.Manager
|
||||
projectCtl project.Controller
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) CreateLabel(ctx context.Context, params operation.CreateLabelParams) middleware.Responder {
|
||||
label := &pkg_model.Label{}
|
||||
lib.JSONCopy(label, params.Label)
|
||||
|
||||
label.Level = common.LabelLevelUser
|
||||
if label.Scope == common.LabelScopeGlobal {
|
||||
label.ProjectID = 0
|
||||
}
|
||||
|
||||
if err := lAPI.requireAccess(ctx, label, rbac.ActionCreate); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
id, err := lAPI.labelMgr.Create(ctx, label)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
|
||||
return operation.NewCreateLabelCreated().WithLocation(location)
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) GetLabelByID(ctx context.Context, params operation.GetLabelByIDParams) middleware.Responder {
|
||||
label, err := lAPI.labelMgr.Get(ctx, params.LabelID)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if label == nil || label.Deleted {
|
||||
return lAPI.SendError(ctx, errors.New(nil).WithMessage("label %d not found", params.LabelID).WithCode(errors.NotFoundCode))
|
||||
}
|
||||
|
||||
if err := lAPI.requireAccess(ctx, label, rbac.ActionRead); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
return operation.NewGetLabelByIDOK().WithPayload(model.NewLabel(label).ToSwagger())
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) ListLabels(ctx context.Context, params operation.ListLabelsParams) middleware.Responder {
|
||||
query, err := lAPI.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
scope := lib.StringValue(params.Scope)
|
||||
if scope != common.LabelScopeGlobal && scope != common.LabelScopeProject {
|
||||
return lAPI.SendError(ctx, errors.New(nil).WithMessage("invalid scope: %s", scope).WithCode(errors.BadRequestCode))
|
||||
}
|
||||
query.Keywords["Level"] = common.LabelLevelUser
|
||||
query.Keywords["Scope"] = scope
|
||||
name := lib.StringValue(params.Name)
|
||||
if name != "" {
|
||||
query.Keywords["name"] = &q.FuzzyMatchValue{Value: name}
|
||||
}
|
||||
if scope == common.LabelScopeProject {
|
||||
pid := lib.Int64Value(params.ProjectID)
|
||||
if pid == 0 {
|
||||
return lAPI.SendError(ctx, errors.BadRequestError(nil).WithMessage("must with project ID when to query project labels"))
|
||||
}
|
||||
if err := lAPI.RequireProjectAccess(ctx, pid, rbac.ActionList, rbac.ResourceLabel); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
query.Keywords["ProjectID"] = pid
|
||||
}
|
||||
|
||||
results := make([]*models.Label, 0)
|
||||
total, err := lAPI.labelMgr.Count(ctx, query)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if total > 0 {
|
||||
labels, err := lAPI.labelMgr.List(ctx, query)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
for _, l := range labels {
|
||||
results = append(results, model.NewLabel(l).ToSwagger())
|
||||
}
|
||||
}
|
||||
|
||||
return operation.NewListLabelsOK().
|
||||
WithXTotalCount(total).
|
||||
WithLink(lAPI.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||
WithPayload(results)
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) UpdateLabel(ctx context.Context, params operation.UpdateLabelParams) middleware.Responder {
|
||||
labelData := &pkg_model.Label{}
|
||||
lib.JSONCopy(labelData, params.Label)
|
||||
|
||||
label, err := lAPI.labelMgr.Get(ctx, params.LabelID)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if label == nil || label.Deleted {
|
||||
return lAPI.SendError(ctx, errors.New(nil).WithMessage("label %d not found", params.LabelID).WithCode(errors.NotFoundCode))
|
||||
}
|
||||
|
||||
if err := lAPI.requireAccess(ctx, label, rbac.ActionUpdate); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
label.Name = labelData.Name
|
||||
label.Description = labelData.Description
|
||||
label.Color = labelData.Color
|
||||
|
||||
if err := label.Valid(); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
if err := lAPI.labelMgr.Update(ctx, label); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
return operation.NewUpdateLabelOK()
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) DeleteLabel(ctx context.Context, params operation.DeleteLabelParams) middleware.Responder {
|
||||
label, err := lAPI.labelMgr.Get(ctx, params.LabelID)
|
||||
if err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if err := lAPI.requireAccess(ctx, label, rbac.ActionDelete); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
id := label.ID
|
||||
// TODO remove this step once chart-museum is removed.
|
||||
if err := dao.DeleteResourceLabelByLabel(id); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if err := lAPI.labelMgr.RemoveFromAllArtifacts(ctx, id); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
if err := lAPI.labelMgr.Delete(ctx, id); err != nil {
|
||||
return lAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
return operation.NewDeleteLabelOK()
|
||||
}
|
||||
|
||||
func (lAPI *labelAPI) requireAccess(ctx context.Context, label *pkg_model.Label, action rbac.Action, subresources ...rbac.Resource) error {
|
||||
switch label.Scope {
|
||||
case common.LabelScopeGlobal:
|
||||
resource := system.NewNamespace().Resource(rbac.ResourceLabel)
|
||||
return lAPI.RequireSystemAccess(ctx, action, resource)
|
||||
case common.LabelScopeProject:
|
||||
if len(subresources) == 0 {
|
||||
subresources = append(subresources, rbac.ResourceLabel)
|
||||
}
|
||||
return lAPI.RequireProjectAccess(ctx, label.ProjectID, action, subresources...)
|
||||
}
|
||||
return errors.New("unsupported label scope").WithCode(errors.BadRequestCode)
|
||||
}
|
|
@ -2,13 +2,13 @@ package model
|
|||
|
||||
import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
common_models "github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/label/model"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||
)
|
||||
|
||||
// Label model
|
||||
type Label struct {
|
||||
*common_models.Label
|
||||
*model.Label
|
||||
}
|
||||
|
||||
// ToSwagger converts the label to the swagger model
|
||||
|
@ -26,6 +26,6 @@ func (l *Label) ToSwagger() *models.Label {
|
|||
}
|
||||
|
||||
// NewLabel ...
|
||||
func NewLabel(l *common_models.Label) *Label {
|
||||
func NewLabel(l *model.Label) *Label {
|
||||
return &Label{Label: l}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,6 @@ func registerLegacyRoutes() {
|
|||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/", &api.MetadataAPI{}, "post:Post")
|
||||
beego.Router("/api/"+version+"/statistics", &api.StatisticAPI{})
|
||||
beego.Router("/api/"+version+"/labels", &api.LabelAPI{}, "post:Post;get:List")
|
||||
beego.Router("/api/"+version+"/labels/:id([0-9]+)", &api.LabelAPI{}, "get:Get;put:Put;delete:Delete")
|
||||
|
||||
// APIs for chart repository
|
||||
if config.WithChartMuseum() {
|
||||
|
|
213
src/testing/pkg/label/dao/dao.go
Normal file
213
src/testing/pkg/label/dao/dao.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
model "github.com/goharbor/harbor/src/pkg/label/model"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
)
|
||||
|
||||
// DAO is an autogenerated mock type for the DAO type
|
||||
type DAO struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, label
|
||||
func (_m *DAO) Create(ctx context.Context, label *model.Label) (int64, error) {
|
||||
ret := _m.Called(ctx, label)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Label) int64); ok {
|
||||
r0 = rf(ctx, label)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.Label) error); ok {
|
||||
r1 = rf(ctx, label)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateReference provides a mock function with given fields: ctx, reference
|
||||
func (_m *DAO) CreateReference(ctx context.Context, reference *model.Reference) (int64, error) {
|
||||
ret := _m.Called(ctx, reference)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Reference) int64); ok {
|
||||
r0 = rf(ctx, reference)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.Reference) error); ok {
|
||||
r1 = rf(ctx, reference)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) Delete(ctx context.Context, id int64) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteReference provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) DeleteReference(ctx context.Context, id int64) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteReferences provides a mock function with given fields: ctx, query
|
||||
func (_m *DAO) DeleteReferences(ctx context.Context, query *q.Query) (int64, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *DAO) Get(ctx context.Context, id int64) (*model.Label, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 *model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *model.Label); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Label, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.Label); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListByArtifact provides a mock function with given fields: ctx, artifactID
|
||||
func (_m *DAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) {
|
||||
ret := _m.Called(ctx, artifactID)
|
||||
|
||||
var r0 []*model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) []*model.Label); ok {
|
||||
r0 = rf(ctx, artifactID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, artifactID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, label
|
||||
func (_m *DAO) Update(ctx context.Context, label *model.Label) error {
|
||||
ret := _m.Called(ctx, label)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Label) error); ok {
|
||||
r0 = rf(ctx, label)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
|
@ -1,70 +1,213 @@
|
|||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package label
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/stretchr/testify/mock"
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
model "github.com/goharbor/harbor/src/pkg/label/model"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
)
|
||||
|
||||
// FakeManager is a fake label manager that implement the src/pkg/label.Manager interface
|
||||
type FakeManager struct {
|
||||
// Manager is an autogenerated mock type for the Manager type
|
||||
type Manager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (f *FakeManager) Get(ctx context.Context, id int64) (*models.Label, error) {
|
||||
args := f.Called()
|
||||
var label *models.Label
|
||||
if args.Get(0) != nil {
|
||||
label = args.Get(0).(*models.Label)
|
||||
// AddTo provides a mock function with given fields: ctx, labelID, artifactID
|
||||
func (_m *Manager) AddTo(ctx context.Context, labelID int64, artifactID int64) error {
|
||||
ret := _m.Called(ctx, labelID, artifactID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok {
|
||||
r0 = rf(ctx, labelID, artifactID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return label, args.Error(1)
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ListByArtifact ...
|
||||
func (f *FakeManager) ListByArtifact(ctx context.Context, artifactID int64) ([]*models.Label, error) {
|
||||
args := f.Called()
|
||||
var labels []*models.Label
|
||||
if args.Get(0) != nil {
|
||||
labels = args.Get(0).([]*models.Label)
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
return labels, args.Error(1)
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AddTo ...
|
||||
func (f *FakeManager) AddTo(ctx context.Context, labelID int64, artifactID int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Create provides a mock function with given fields: ctx, _a1
|
||||
func (_m *Manager) Create(ctx context.Context, _a1 *model.Label) (int64, error) {
|
||||
ret := _m.Called(ctx, _a1)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Label) int64); ok {
|
||||
r0 = rf(ctx, _a1)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.Label) error); ok {
|
||||
r1 = rf(ctx, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveFrom ...
|
||||
func (f *FakeManager) RemoveFrom(ctx context.Context, labelID int64, artifactID int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
func (_m *Manager) Delete(ctx context.Context, id int64) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RemoveAllFrom ...
|
||||
func (f *FakeManager) RemoveAllFrom(ctx context.Context, artifactID int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *Manager) Get(ctx context.Context, id int64) (*model.Label, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 *model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *model.Label); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveFromAllArtifacts ...
|
||||
func (f *FakeManager) RemoveFromAllArtifacts(ctx context.Context, labelID int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Label, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.Label); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListByArtifact provides a mock function with given fields: ctx, artifactID
|
||||
func (_m *Manager) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) {
|
||||
ret := _m.Called(ctx, artifactID)
|
||||
|
||||
var r0 []*model.Label
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) []*model.Label); ok {
|
||||
r0 = rf(ctx, artifactID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.Label)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, artifactID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveAllFrom provides a mock function with given fields: ctx, artifactID
|
||||
func (_m *Manager) RemoveAllFrom(ctx context.Context, artifactID int64) error {
|
||||
ret := _m.Called(ctx, artifactID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, artifactID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RemoveFrom provides a mock function with given fields: ctx, labelID, artifactID
|
||||
func (_m *Manager) RemoveFrom(ctx context.Context, labelID int64, artifactID int64) error {
|
||||
ret := _m.Called(ctx, labelID, artifactID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok {
|
||||
r0 = rf(ctx, labelID, artifactID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RemoveFromAllArtifacts provides a mock function with given fields: ctx, labelID
|
||||
func (_m *Manager) RemoveFromAllArtifacts(ctx context.Context, labelID int64) error {
|
||||
ret := _m.Called(ctx, labelID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, labelID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, _a1
|
||||
func (_m *Manager) Update(ctx context.Context, _a1 *model.Label) error {
|
||||
ret := _m.Called(ctx, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Label) error); ok {
|
||||
r0 = rf(ctx, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
|
|
@ -47,3 +47,5 @@ package pkg
|
|||
//go:generate mockery --case snake --dir ../../pkg/reg/adapter --name Adapter --output ./reg/adapter --outpkg adapter
|
||||
//go:generate mockery --case snake --dir ../../pkg/replication/dao --name DAO --output ./replication/dao --outpkg dao
|
||||
//go:generate mockery --case snake --dir ../../pkg/replication --name Manager --output ./replication --outpkg manager
|
||||
//go:generate mockery --case snake --dir ../../pkg/label --name Manager --output ./label --outpkg label
|
||||
//go:generate mockery --case snake --dir ../../pkg/label/dao --name DAO --output ./label/dao --outpkg dao
|
||||
|
|
|
@ -31,7 +31,7 @@ def _create_client(server, credential, debug, api_type="products"):
|
|||
cfg = None
|
||||
if api_type in ('projectv2', 'artifact', 'repository', 'scanner', 'scan', 'scanall', 'preheat', 'quota',
|
||||
'replication', 'registry', 'robot', 'gc', 'retention', 'immutable', 'system_cve_allowlist',
|
||||
'configure', 'user', 'member', 'health'):
|
||||
'configure', 'user', 'member', 'health', 'label'):
|
||||
cfg = v2_swagger_client.Configuration()
|
||||
else:
|
||||
cfg = swagger_client.Configuration()
|
||||
|
@ -72,6 +72,7 @@ def _create_client(server, credential, debug, api_type="products"):
|
|||
"immutable": v2_swagger_client.ImmutableApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"system_cve_allowlist": v2_swagger_client.SystemCVEAllowlistApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"configure": v2_swagger_client.ConfigureApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"label": v2_swagger_client.LabelApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"user": v2_swagger_client.UserApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"member": v2_swagger_client.MemberApi(v2_swagger_client.ApiClient(cfg)),
|
||||
"health": v2_swagger_client.HealthApi(v2_swagger_client.ApiClient(cfg)),
|
||||
|
|
|
@ -2,21 +2,24 @@
|
|||
|
||||
import sys
|
||||
import base
|
||||
import swagger_client
|
||||
from swagger_client.rest import ApiException
|
||||
import v2_swagger_client
|
||||
from v2_swagger_client.rest import ApiException
|
||||
|
||||
class Label(base.Base, object):
|
||||
def __init__(self):
|
||||
super(Label,self).__init__(api_type = "label")
|
||||
|
||||
class Label(base.Base):
|
||||
def create_label(self, name=None, desc="", color="", scope="g",
|
||||
project_id=0, expect_status_code = 201, **kwargs):
|
||||
if name is None:
|
||||
name = base._random_name("label")
|
||||
label = swagger_client.Label(name=name,
|
||||
label = v2_swagger_client.Label(name=name,
|
||||
description=desc, color=color,
|
||||
scope=scope, project_id=project_id)
|
||||
client = self._get_client(**kwargs)
|
||||
|
||||
try:
|
||||
_, status_code, header = client.labels_post_with_http_info(label)
|
||||
_, status_code, header = client.create_label_with_http_info(label)
|
||||
except ApiException as e:
|
||||
base._assert_status_code(expect_status_code, e.status)
|
||||
else:
|
||||
|
@ -26,4 +29,4 @@ class Label(base.Base):
|
|||
|
||||
def delete_label(self, label_id, **kwargs):
|
||||
client = self._get_client(**kwargs)
|
||||
return client.labels_id_delete_with_http_info(int(label_id))
|
||||
return client.delete_label_with_http_info(int(label_id))
|
Loading…
Reference in New Issue
Block a user