mirror of
https://github.com/harness/drone.git
synced 2025-05-20 19:09:59 +08:00
Add label feature (#2073)
* Address PR comments * Add list label from ancestor scopes query param * Add label feature
This commit is contained in:
parent
d36bf54f99
commit
81fcd524c8
@ -26,6 +26,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/codecomments"
|
||||
"github.com/harness/gitness/app/services/codeowners"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
locker "github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/pullreq"
|
||||
@ -65,6 +66,7 @@ type Controller struct {
|
||||
codeOwners *codeowners.Service
|
||||
locker *locker.Locker
|
||||
importer *importer.PullReq
|
||||
labelSvc *label.Service
|
||||
}
|
||||
|
||||
func NewController(
|
||||
@ -91,6 +93,7 @@ func NewController(
|
||||
codeowners *codeowners.Service,
|
||||
locker *locker.Locker,
|
||||
importer *importer.PullReq,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
tx: tx,
|
||||
@ -116,6 +119,7 @@ func NewController(
|
||||
codeOwners: codeowners,
|
||||
locker: locker,
|
||||
importer: importer,
|
||||
labelSvc: labelSvc,
|
||||
}
|
||||
}
|
||||
|
||||
|
55
app/api/controller/pullreq/label_assign.go
Normal file
55
app/api/controller/pullreq/label_assign.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// AssignLabel assigns a label to a pull request .
|
||||
func (c *Controller) AssignLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
pullreqNum int64,
|
||||
in *types.PullReqCreateInput,
|
||||
) (*types.PullReqLabel, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to target repo: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
pullreq, err := c.pullreqStore.FindByNumber(ctx, repo.ID, pullreqNum)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find pullreq: %w", err)
|
||||
}
|
||||
|
||||
pullreqLabel, err := c.labelSvc.AssignToPullReq(
|
||||
ctx, session.Principal.ID, pullreq.ID, repo.ID, repo.ParentID, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create pullreq label: %w", err)
|
||||
}
|
||||
|
||||
return pullreqLabel, nil
|
||||
}
|
51
app/api/controller/pullreq/label_list.go
Normal file
51
app/api/controller/pullreq/label_list.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ListLabels list labels assigned to a specified pullreq.
|
||||
func (c *Controller) ListLabels(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
pullreqNum int64,
|
||||
filter *types.AssignableLabelFilter,
|
||||
) (*types.ScopesLabels, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to target repo: %w", err)
|
||||
}
|
||||
|
||||
pullreq, err := c.pullreqStore.FindByNumber(ctx, repo.ID, pullreqNum)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find pullreq: %w", err)
|
||||
}
|
||||
|
||||
scopeLabelsMap, err := c.labelSvc.ListPullReqLabels(
|
||||
ctx, repo, repo.ParentID, pullreq.ID, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list pullreq labels: %w", err)
|
||||
}
|
||||
|
||||
return scopeLabelsMap, nil
|
||||
}
|
49
app/api/controller/pullreq/label_unassign.go
Normal file
49
app/api/controller/pullreq/label_unassign.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UnassignLabel removes a label from a pull request.
|
||||
func (c *Controller) UnassignLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
pullreqNum int64,
|
||||
labelID int64,
|
||||
) error {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoPush)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire access to target repo: %w", err)
|
||||
}
|
||||
|
||||
pullreq, err := c.pullreqStore.FindByNumber(ctx, repo.ID, pullreqNum)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find pullreq: %w", err)
|
||||
}
|
||||
|
||||
if err := c.labelSvc.UnassignFromPullReq(
|
||||
ctx, repo.ID, repo.ParentID, pullreq.ID, labelID); err != nil {
|
||||
return fmt.Errorf("failed to delete pullreq label: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/codecomments"
|
||||
"github.com/harness/gitness/app/services/codeowners"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/pullreq"
|
||||
@ -41,21 +42,24 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer
|
||||
pullReqStore store.PullReqStore, pullReqActivityStore store.PullReqActivityStore,
|
||||
codeCommentsView store.CodeCommentView,
|
||||
pullReqReviewStore store.PullReqReviewStore, pullReqReviewerStore store.PullReqReviewerStore,
|
||||
repoStore store.RepoStore, principalStore store.PrincipalStore, principalInfoCache store.PrincipalInfoCache,
|
||||
repoStore store.RepoStore,
|
||||
principalStore store.PrincipalStore, principalInfoCache store.PrincipalInfoCache,
|
||||
fileViewStore store.PullReqFileViewStore, membershipStore store.MembershipStore,
|
||||
checkStore store.CheckStore,
|
||||
rpcClient git.Interface, eventReporter *pullreqevents.Reporter, codeCommentMigrator *codecomments.Migrator,
|
||||
pullreqService *pullreq.Service, ruleManager *protection.Manager, sseStreamer sse.Streamer,
|
||||
codeOwners *codeowners.Service, locker *locker.Locker, importer *importer.PullReq,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return NewController(tx, urlProvider, authorizer,
|
||||
pullReqStore, pullReqActivityStore,
|
||||
codeCommentsView,
|
||||
pullReqReviewStore, pullReqReviewerStore,
|
||||
repoStore, principalStore, principalInfoCache,
|
||||
repoStore,
|
||||
principalStore, principalInfoCache,
|
||||
fileViewStore, membershipStore,
|
||||
checkStore,
|
||||
rpcClient, eventReporter,
|
||||
codeCommentMigrator,
|
||||
pullreqService, ruleManager, sseStreamer, codeOwners, locker, importer)
|
||||
pullreqService, ruleManager, sseStreamer, codeOwners, locker, importer, labelSvc)
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/codeowners"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/publicaccess"
|
||||
@ -92,6 +93,7 @@ type Controller struct {
|
||||
identifierCheck check.RepoIdentifier
|
||||
repoCheck Check
|
||||
publicAccess publicaccess.Service
|
||||
labelSvc *label.Service
|
||||
}
|
||||
|
||||
func NewController(
|
||||
@ -119,6 +121,7 @@ func NewController(
|
||||
identifierCheck check.RepoIdentifier,
|
||||
repoCheck Check,
|
||||
publicAccess publicaccess.Service,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
defaultBranch: config.Git.DefaultBranch,
|
||||
@ -145,6 +148,7 @@ func NewController(
|
||||
identifierCheck: identifierCheck,
|
||||
repoCheck: repoCheck,
|
||||
publicAccess: publicAccess,
|
||||
labelSvc: labelSvc,
|
||||
}
|
||||
}
|
||||
|
||||
|
49
app/api/controller/repo/label_define.go
Normal file
49
app/api/controller/repo/label_define.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DefineLabel defines a new label for the specified repository.
|
||||
func (c *Controller) DefineLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
in *types.DefineLabelInput,
|
||||
) (*types.Label, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Define(
|
||||
ctx, session.Principal.ID, nil, &repo.ID, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create repo label: %w", err)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
42
app/api/controller/repo/label_delete.go
Normal file
42
app/api/controller/repo/label_delete.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DeleteLabel deletes a label for the specified repository.
|
||||
func (c *Controller) DeleteLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
) error {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := c.labelSvc.Delete(ctx, nil, &repo.ID, key); err != nil {
|
||||
return fmt.Errorf("failed to delete repo label: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
44
app/api/controller/repo/label_list.go
Normal file
44
app/api/controller/repo/label_list.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ListLabels lists all labels defined for the specified repository.
|
||||
func (c *Controller) ListLabels(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
labels, err := c.labelSvc.List(ctx, &repo.ParentID, &repo.ID, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repo labels: %w", err)
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
}
|
49
app/api/controller/repo/label_save.go
Normal file
49
app/api/controller/repo/label_save.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// SaveLabel creates or updates a label and possibly label values for the specified repository.
|
||||
func (c *Controller) SaveLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
in *types.SaveInput,
|
||||
) (*types.LabelWithValues, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
labelWithValues, err := c.labelSvc.Save(
|
||||
ctx, session.Principal.ID, nil, &repo.ID, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save label: %w", err)
|
||||
}
|
||||
|
||||
return labelWithValues, nil
|
||||
}
|
49
app/api/controller/repo/label_update.go
Normal file
49
app/api/controller/repo/label_update.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UpdateLabel updates a label for the specified repository.
|
||||
func (c *Controller) UpdateLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
in *types.UpdateLabelInput,
|
||||
) (*types.Label, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Update(ctx, session.Principal.ID, nil, &repo.ID, key, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update repo label: %w", err)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
65
app/api/controller/repo/label_value_define.go
Normal file
65
app/api/controller/repo/label_value_define.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DefineLabelValue defines a new label value for the specified repository.
|
||||
func (c *Controller) DefineLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
in *types.DefineValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
repo, err := GetRepo(ctx, c.repoStore, repoRef, []enum.RepoState{enum.RepoStateActive})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Find(ctx, nil, &repo.ID, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo label: %w", err)
|
||||
}
|
||||
|
||||
permission := enum.PermissionRepoEdit
|
||||
if label.Type == enum.LabelTypeDynamic {
|
||||
permission = enum.PermissionRepoPush
|
||||
}
|
||||
|
||||
if err = apiauth.CheckRepo(
|
||||
ctx, c.authorizer, session, repo, permission); err != nil {
|
||||
return nil, fmt.Errorf("access check failed: %w", err)
|
||||
}
|
||||
|
||||
value, err := c.labelSvc.DefineValue(ctx, session.Principal.ID, label.ID, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create repo label value: %w", err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
43
app/api/controller/repo/label_value_delete.go
Normal file
43
app/api/controller/repo/label_value_delete.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DeleteLabelValue deletes a label value for the specified repository.
|
||||
func (c *Controller) DeleteLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
value string,
|
||||
) error {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
if err := c.labelSvc.DeleteValue(ctx, nil, &repo.ID, key, value); err != nil {
|
||||
return fmt.Errorf("failed to delete repo label value: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
45
app/api/controller/repo/label_value_list.go
Normal file
45
app/api/controller/repo/label_value_list.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ListLabelValues lists all label values defined for the specified repository.
|
||||
func (c *Controller) ListLabelValues(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
filter *types.ListQueryFilter,
|
||||
) ([]*types.LabelValue, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
values, err := c.labelSvc.ListValues(ctx, nil, &repo.ID, key, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repo label values: %w", err)
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
52
app/api/controller/repo/label_value_update.go
Normal file
52
app/api/controller/repo/label_value_update.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UpdateLabelValue updates a label value for the specified label and repository.
|
||||
func (c *Controller) UpdateLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
key string,
|
||||
value string,
|
||||
in *types.UpdateValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Find(ctx, nil, &repo.ID, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo label: %w", err)
|
||||
}
|
||||
|
||||
labelValue, err := c.labelSvc.UpdateValue(
|
||||
ctx, session.Principal.ID, label.ID, value, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update repo label value: %w", err)
|
||||
}
|
||||
|
||||
return labelValue, nil
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/codeowners"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/publicaccess"
|
||||
@ -67,13 +68,14 @@ func ProvideController(
|
||||
identifierCheck check.RepoIdentifier,
|
||||
repoChecks Check,
|
||||
publicAccess publicaccess.Service,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return NewController(config, tx, urlProvider,
|
||||
authorizer,
|
||||
repoStore, spaceStore, pipelineStore,
|
||||
principalStore, ruleStore, settings, principalInfoCache, protectionManager, rpcClient, importer,
|
||||
codeOwners, reporeporter, indexer, limiter, locker, auditService, mtxManager, identifierCheck,
|
||||
repoChecks, publicAccess)
|
||||
repoChecks, publicAccess, labelSvc)
|
||||
}
|
||||
|
||||
func ProvideRepoCheck() Check {
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/exporter"
|
||||
"github.com/harness/gitness/app/services/gitspace"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/publicaccess"
|
||||
"github.com/harness/gitness/app/sse"
|
||||
"github.com/harness/gitness/app/store"
|
||||
@ -62,27 +63,30 @@ func (s SpaceOutput) MarshalJSON() ([]byte, error) {
|
||||
type Controller struct {
|
||||
nestedSpacesEnabled bool
|
||||
|
||||
tx dbtx.Transactor
|
||||
urlProvider url.Provider
|
||||
sseStreamer sse.Streamer
|
||||
identifierCheck check.SpaceIdentifier
|
||||
authorizer authz.Authorizer
|
||||
spacePathStore store.SpacePathStore
|
||||
pipelineStore store.PipelineStore
|
||||
secretStore store.SecretStore
|
||||
connectorStore store.ConnectorStore
|
||||
templateStore store.TemplateStore
|
||||
spaceStore store.SpaceStore
|
||||
repoStore store.RepoStore
|
||||
principalStore store.PrincipalStore
|
||||
repoCtrl *repo.Controller
|
||||
membershipStore store.MembershipStore
|
||||
importer *importer.Repository
|
||||
exporter *exporter.Repository
|
||||
resourceLimiter limiter.ResourceLimiter
|
||||
publicAccess publicaccess.Service
|
||||
auditService audit.Service
|
||||
gitspaceSvc *gitspace.Service
|
||||
tx dbtx.Transactor
|
||||
urlProvider url.Provider
|
||||
sseStreamer sse.Streamer
|
||||
identifierCheck check.SpaceIdentifier
|
||||
authorizer authz.Authorizer
|
||||
spacePathStore store.SpacePathStore
|
||||
pipelineStore store.PipelineStore
|
||||
secretStore store.SecretStore
|
||||
connectorStore store.ConnectorStore
|
||||
templateStore store.TemplateStore
|
||||
spaceStore store.SpaceStore
|
||||
repoStore store.RepoStore
|
||||
principalStore store.PrincipalStore
|
||||
repoCtrl *repo.Controller
|
||||
membershipStore store.MembershipStore
|
||||
importer *importer.Repository
|
||||
exporter *exporter.Repository
|
||||
resourceLimiter limiter.ResourceLimiter
|
||||
publicAccess publicaccess.Service
|
||||
auditService audit.Service
|
||||
gitspaceSvc *gitspace.Service
|
||||
gitspaceConfigStore store.GitspaceConfigStore
|
||||
gitspaceInstanceStore store.GitspaceInstanceStore
|
||||
labelSvc *label.Service
|
||||
}
|
||||
|
||||
func NewController(config *types.Config, tx dbtx.Transactor, urlProvider url.Provider,
|
||||
@ -93,29 +97,34 @@ func NewController(config *types.Config, tx dbtx.Transactor, urlProvider url.Pro
|
||||
membershipStore store.MembershipStore, importer *importer.Repository, exporter *exporter.Repository,
|
||||
limiter limiter.ResourceLimiter, publicAccess publicaccess.Service, auditService audit.Service,
|
||||
gitspaceSvc *gitspace.Service,
|
||||
gitspaceStore store.GitspaceConfigStore, gitspaceInstanceStore store.GitspaceInstanceStore,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
nestedSpacesEnabled: config.NestedSpacesEnabled,
|
||||
tx: tx,
|
||||
urlProvider: urlProvider,
|
||||
sseStreamer: sseStreamer,
|
||||
identifierCheck: identifierCheck,
|
||||
authorizer: authorizer,
|
||||
spacePathStore: spacePathStore,
|
||||
pipelineStore: pipelineStore,
|
||||
secretStore: secretStore,
|
||||
connectorStore: connectorStore,
|
||||
templateStore: templateStore,
|
||||
spaceStore: spaceStore,
|
||||
repoStore: repoStore,
|
||||
principalStore: principalStore,
|
||||
repoCtrl: repoCtrl,
|
||||
membershipStore: membershipStore,
|
||||
importer: importer,
|
||||
exporter: exporter,
|
||||
resourceLimiter: limiter,
|
||||
publicAccess: publicAccess,
|
||||
auditService: auditService,
|
||||
gitspaceSvc: gitspaceSvc,
|
||||
nestedSpacesEnabled: config.NestedSpacesEnabled,
|
||||
tx: tx,
|
||||
urlProvider: urlProvider,
|
||||
sseStreamer: sseStreamer,
|
||||
identifierCheck: identifierCheck,
|
||||
authorizer: authorizer,
|
||||
spacePathStore: spacePathStore,
|
||||
pipelineStore: pipelineStore,
|
||||
secretStore: secretStore,
|
||||
connectorStore: connectorStore,
|
||||
templateStore: templateStore,
|
||||
spaceStore: spaceStore,
|
||||
repoStore: repoStore,
|
||||
principalStore: principalStore,
|
||||
repoCtrl: repoCtrl,
|
||||
membershipStore: membershipStore,
|
||||
importer: importer,
|
||||
exporter: exporter,
|
||||
resourceLimiter: limiter,
|
||||
publicAccess: publicAccess,
|
||||
auditService: auditService,
|
||||
gitspaceSvc: gitspaceSvc,
|
||||
gitspaceConfigStore: gitspaceStore,
|
||||
gitspaceInstanceStore: gitspaceInstanceStore,
|
||||
labelSvc: labelSvc,
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +43,12 @@ type ImportRepositoriesOutput struct {
|
||||
DuplicateRepos []*repoctrl.RepositoryOutput `json:"duplicate_repos"` // repos which already exist in the space.
|
||||
}
|
||||
|
||||
// getSpaceCheckAuthRepoCreation checks whether the user has permissions to create repos
|
||||
// in the given space.
|
||||
func (c *Controller) getSpaceCheckAuthRepoCreation(
|
||||
// getSpaceCheckAuth checks whether the user has repo permissions permission.
|
||||
func (c *Controller) getSpaceCheckAuth(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
permission enum.Permission,
|
||||
) (*types.Space, error) {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
@ -62,7 +62,7 @@ func (c *Controller) getSpaceCheckAuthRepoCreation(
|
||||
Identifier: "",
|
||||
}
|
||||
|
||||
err = apiauth.Check(ctx, c.authorizer, session, scope, resource, enum.PermissionRepoEdit)
|
||||
err = apiauth.Check(ctx, c.authorizer, session, scope, resource, permission)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("auth check failed: %w", err)
|
||||
}
|
||||
@ -80,7 +80,7 @@ func (c *Controller) ImportRepositories(
|
||||
spaceRef string,
|
||||
in *ImportRepositoriesInput,
|
||||
) (ImportRepositoriesOutput, error) {
|
||||
space, err := c.getSpaceCheckAuthRepoCreation(ctx, session, spaceRef)
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionRepoEdit)
|
||||
if err != nil {
|
||||
return ImportRepositoriesOutput{}, err
|
||||
}
|
||||
|
49
app/api/controller/space/label_define.go
Normal file
49
app/api/controller/space/label_define.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DefineLabel defines a new label for the specified space.
|
||||
func (c *Controller) DefineLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
in *types.DefineLabelInput,
|
||||
) (*types.Label, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Define(
|
||||
ctx, session.Principal.ID, &space.ID, nil, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create space label: %w", err)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
42
app/api/controller/space/label_delete.go
Normal file
42
app/api/controller/space/label_delete.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DeleteLabel deletes a label for the specified space.
|
||||
func (c *Controller) DeleteLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
) error {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := c.labelSvc.Delete(ctx, &space.ID, nil, key); err != nil {
|
||||
return fmt.Errorf("failed to delete space label: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
44
app/api/controller/space/label_list.go
Normal file
44
app/api/controller/space/label_list.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ListLabels lists all labels defined for the specified space.
|
||||
func (c *Controller) ListLabels(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
labels, err := c.labelSvc.List(ctx, &space.ID, nil, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list space labels: %w", err)
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
}
|
49
app/api/controller/space/label_save.go
Normal file
49
app/api/controller/space/label_save.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// SaveLabel defines a new label for the specified space.
|
||||
func (c *Controller) SaveLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
in *types.SaveInput,
|
||||
) (*types.LabelWithValues, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
labelWithValues, err := c.labelSvc.Save(
|
||||
ctx, session.Principal.ID, &space.ID, nil, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save label: %w", err)
|
||||
}
|
||||
|
||||
return labelWithValues, nil
|
||||
}
|
49
app/api/controller/space/label_update.go
Normal file
49
app/api/controller/space/label_update.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UpdateLabel updates a label for the specified space.
|
||||
func (c *Controller) UpdateLabel(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
in *types.UpdateLabelInput,
|
||||
) (*types.Label, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Update(ctx, session.Principal.ID, &space.ID, nil, key, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update space label: %w", err)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
55
app/api/controller/space/label_value_define.go
Normal file
55
app/api/controller/space/label_value_define.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DefineLabelValue defines a new label value for the specified space and label.
|
||||
func (c *Controller) DefineLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
in *types.DefineValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
// TODO: permission check should be based on static vs dynamic label
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := in.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate input: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Find(ctx, &space.ID, nil, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo label: %w", err)
|
||||
}
|
||||
|
||||
value, err := c.labelSvc.DefineValue(ctx, session.Principal.ID, label.ID, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create space label value: %w", err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
43
app/api/controller/space/label_value_delete.go
Normal file
43
app/api/controller/space/label_value_delete.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// DeleteLabelValue deletes a label value for the specified space.
|
||||
func (c *Controller) DeleteLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
value string,
|
||||
) error {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
if err := c.labelSvc.DeleteValue(ctx, &space.ID, nil, key, value); err != nil {
|
||||
return fmt.Errorf("failed to delete space label value: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
45
app/api/controller/space/label_value_list.go
Normal file
45
app/api/controller/space/label_value_list.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ListLabelValues lists all label values defined in the specified space.
|
||||
func (c *Controller) ListLabelValues(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
filter *types.ListQueryFilter,
|
||||
) ([]*types.LabelValue, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceView)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
values, err := c.labelSvc.ListValues(ctx, &space.ID, nil, key, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list space label values: %w", err)
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
52
app/api/controller/space/label_value_update.go
Normal file
52
app/api/controller/space/label_value_update.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UpdateLabelValue updates a label value for the specified space and label.
|
||||
func (c *Controller) UpdateLabelValue(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
spaceRef string,
|
||||
key string,
|
||||
value string,
|
||||
in *types.UpdateValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
space, err := c.getSpaceCheckAuth(ctx, session, spaceRef, enum.PermissionSpaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
|
||||
}
|
||||
|
||||
label, err := c.labelSvc.Find(ctx, &space.ID, nil, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find space label: %w", err)
|
||||
}
|
||||
|
||||
labelValue, err := c.labelSvc.UpdateValue(
|
||||
ctx, session.Principal.ID, label.ID, value, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update space label value: %w", err)
|
||||
}
|
||||
|
||||
return labelValue, nil
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/exporter"
|
||||
"github.com/harness/gitness/app/services/gitspace"
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/publicaccess"
|
||||
"github.com/harness/gitness/app/sse"
|
||||
"github.com/harness/gitness/app/store"
|
||||
@ -46,6 +47,8 @@ func ProvideController(config *types.Config, tx dbtx.Transactor, urlProvider url
|
||||
repoCtrl *repo.Controller, membershipStore store.MembershipStore, importer *importer.Repository,
|
||||
exporter *exporter.Repository, limiter limiter.ResourceLimiter, publicAccess publicaccess.Service,
|
||||
auditService audit.Service, gitspaceService *gitspace.Service,
|
||||
gitspaceConfigStore store.GitspaceConfigStore, instanceStore store.GitspaceInstanceStore,
|
||||
labelSvc *label.Service,
|
||||
) *Controller {
|
||||
return NewController(config, tx, urlProvider, sseStreamer, identifierCheck, authorizer,
|
||||
spacePathStore, pipelineStore, secretStore,
|
||||
@ -53,5 +56,7 @@ func ProvideController(config *types.Config, tx dbtx.Transactor, urlProvider url
|
||||
spaceStore, repoStore, principalStore,
|
||||
repoCtrl, membershipStore, importer,
|
||||
exporter, limiter, publicAccess,
|
||||
auditService, gitspaceService)
|
||||
auditService, gitspaceService,
|
||||
gitspaceConfigStore, instanceStore,
|
||||
labelSvc)
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func NewController(authorizer authz.Authorizer,
|
||||
func (c *Controller) getRepoCheckAccess(ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
reqPermission enum.Permission,
|
||||
permission enum.Permission,
|
||||
) (*types.Repository, error) {
|
||||
if repoRef == "" {
|
||||
return nil, usererror.BadRequest("A valid repository reference must be provided.")
|
||||
@ -75,7 +75,7 @@ func (c *Controller) getRepoCheckAccess(ctx context.Context,
|
||||
return nil, fmt.Errorf("failed to find repo: %w", err)
|
||||
}
|
||||
|
||||
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, reqPermission); err != nil {
|
||||
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, permission); err != nil {
|
||||
return nil, fmt.Errorf("failed to verify authorization: %w", err)
|
||||
}
|
||||
|
||||
|
60
app/api/handler/pullreq/label_assign.go
Normal file
60
app/api/handler/pullreq/label_assign.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleAssignLabel(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.PullReqCreateInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := pullreqCtrl.AssignLabel(
|
||||
ctx, session, repoRef, pullreqNumber, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
56
app/api/handler/pullreq/label_list.go
Normal file
56
app/api/handler/pullreq/label_list.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleListLabels(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
filter, err := request.ParseAssignableLabelFilter(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := pullreqCtrl.ListLabels(ctx, session, repoRef, pullreqNumber, filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, labels)
|
||||
}
|
||||
}
|
56
app/api/handler/pullreq/label_unassign.go
Normal file
56
app/api/handler/pullreq/label_unassign.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 pullreq
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleUnassignLabel(pullreqCtrl *pullreq.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pullreqNumber, err := request.GetPullReqNumberFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
labelID, err := request.GetLabelIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pullreqCtrl.UnassignLabel(
|
||||
ctx, session, repoRef, pullreqNumber, labelID); err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
53
app/api/handler/repo/label_define.go
Normal file
53
app/api/handler/repo/label_define.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleDefineLabel(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.DefineLabelInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := repoCtrl.DefineLabel(ctx, session, repoRef, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, label)
|
||||
}
|
||||
}
|
50
app/api/handler/repo/label_delete.go
Normal file
50
app/api/handler/repo/label_delete.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleDeleteLabel(labelCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = labelCtrl.DeleteLabel(ctx, session, repoRef, key)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
50
app/api/handler/repo/label_list.go
Normal file
50
app/api/handler/repo/label_list.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleListLabels(labelCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
filter, err := request.ParseLabelFilter(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := labelCtrl.ListLabels(ctx, session, repoRef, filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, labels)
|
||||
}
|
||||
}
|
53
app/api/handler/repo/label_save.go
Normal file
53
app/api/handler/repo/label_save.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleSaveLabel(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.SaveInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := repoCtrl.SaveLabel(ctx, session, repoRef, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
59
app/api/handler/repo/label_update.go
Normal file
59
app/api/handler/repo/label_update.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleUpdateLabel(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.UpdateLabelInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := repoCtrl.UpdateLabel(ctx, session, repoRef, key, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
59
app/api/handler/repo/label_value_define.go
Normal file
59
app/api/handler/repo/label_value_define.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleDefineLabelValue(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.DefineValueInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := repoCtrl.DefineLabelValue(ctx, session, repoRef, key, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, label)
|
||||
}
|
||||
}
|
56
app/api/handler/repo/label_value_delete.go
Normal file
56
app/api/handler/repo/label_value_delete.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleDeleteLabelValue(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := request.GetLabelValueFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = repoCtrl.DeleteLabelValue(ctx, session, repoRef, key, value)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
52
app/api/handler/repo/label_value_list.go
Normal file
52
app/api/handler/repo/label_value_list.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleListLabelValues(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
filter := request.ParseListQueryFilterFromRequest(r)
|
||||
|
||||
labels, err := repoCtrl.ListLabelValues(ctx, session, repoRef, key, &filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, labels)
|
||||
}
|
||||
}
|
66
app/api/handler/repo/label_value_update.go
Normal file
66
app/api/handler/repo/label_value_update.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleUpdateLabelValue(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := request.GetLabelValueFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.UpdateValueInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := repoCtrl.UpdateLabelValue(
|
||||
ctx, session, repoRef, key, value, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
53
app/api/handler/space/label_define.go
Normal file
53
app/api/handler/space/label_define.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleDefineLabel(labelCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.DefineLabelInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := labelCtrl.DefineLabel(ctx, session, spaceRef, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, label)
|
||||
}
|
||||
}
|
50
app/api/handler/space/label_delete.go
Normal file
50
app/api/handler/space/label_delete.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleDeleteLabel(labelCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
identifier, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = labelCtrl.DeleteLabel(ctx, session, spaceRef, identifier)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
50
app/api/handler/space/label_list.go
Normal file
50
app/api/handler/space/label_list.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleListLabels(labelCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
filter, err := request.ParseLabelFilter(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := labelCtrl.ListLabels(ctx, session, spaceRef, filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, labels)
|
||||
}
|
||||
}
|
53
app/api/handler/space/label_save.go
Normal file
53
app/api/handler/space/label_save.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleSaveLabel(labelCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.SaveInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := labelCtrl.SaveLabel(ctx, session, spaceRef, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
59
app/api/handler/space/label_update.go
Normal file
59
app/api/handler/space/label_update.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleUpdateLabel(labelCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.UpdateLabelInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := labelCtrl.UpdateLabel(ctx, session, spaceRef, key, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
59
app/api/handler/space/label_value_define.go
Normal file
59
app/api/handler/space/label_value_define.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleDefineLabelValue(spaceCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.DefineValueInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := spaceCtrl.DefineLabelValue(ctx, session, spaceRef, key, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, label)
|
||||
}
|
||||
}
|
56
app/api/handler/space/label_value_delete.go
Normal file
56
app/api/handler/space/label_value_delete.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleDeleteLabelValue(spaceCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := request.GetLabelValueFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = spaceCtrl.DeleteLabelValue(ctx, session, spaceRef, key, value)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
53
app/api/handler/space/label_value_list.go
Normal file
53
app/api/handler/space/label_value_list.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleListLabelValues(labelValueCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
filter := request.ParseListQueryFilterFromRequest(r)
|
||||
|
||||
labels, err := labelValueCtrl.ListLabelValues(
|
||||
ctx, session, spaceRef, key, &filter)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, labels)
|
||||
}
|
||||
}
|
66
app/api/handler/space/label_value_update.go
Normal file
66
app/api/handler/space/label_value_update.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 space
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func HandleUpdateLabelValue(spaceCtrl *space.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := request.GetLabelKeyFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := request.GetLabelValueFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(types.UpdateValueInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
label, err := spaceCtrl.UpdateLabelValue(
|
||||
ctx, session, spaceRef, key, value, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, label)
|
||||
}
|
||||
}
|
@ -142,6 +142,11 @@ type getPullReqChecksRequest struct {
|
||||
pullReqRequest
|
||||
}
|
||||
|
||||
type pullReqAssignLabelInput struct {
|
||||
pullReqRequest
|
||||
types.PullReqCreateInput
|
||||
}
|
||||
|
||||
var queryParameterQueryPullRequest = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamQuery,
|
||||
@ -314,6 +319,21 @@ var queryParameterBeforePullRequestActivity = openapi3.ParameterOrRef{
|
||||
},
|
||||
}
|
||||
|
||||
var queryParameterAssignable = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamAssignable,
|
||||
In: openapi3.ParameterInQuery,
|
||||
Description: ptr.String("The result should contain all labels assignable to the pullreq."),
|
||||
Required: ptr.Bool(false),
|
||||
Schema: &openapi3.SchemaOrRef{
|
||||
Schema: &openapi3.Schema{
|
||||
Type: ptrSchemaType(openapi3.SchemaTypeBoolean),
|
||||
Default: ptrptr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func pullReqOperations(reflector *openapi3.Reflector) {
|
||||
createPullReq := openapi3.Operation{}
|
||||
@ -624,4 +644,45 @@ func pullReqOperations(reflector *openapi3.Reflector) {
|
||||
panicOnErr(reflector.SetJSONResponse(&opChecks, new(usererror.Error), http.StatusForbidden))
|
||||
panicOnErr(reflector.SetJSONResponse(&opChecks, new(usererror.Error), http.StatusNotFound))
|
||||
panicOnErr(reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/pullreq/{pullreq_number}/checks", opChecks))
|
||||
|
||||
opAssignLabel := openapi3.Operation{}
|
||||
opAssignLabel.WithTags("pullreq")
|
||||
opAssignLabel.WithMapOfAnything(map[string]interface{}{"operationId": "assignLabel"})
|
||||
_ = reflector.SetRequest(&opAssignLabel, new(pullReqAssignLabelInput), http.MethodPut)
|
||||
_ = reflector.SetJSONResponse(&opAssignLabel, new(types.PullReqLabel), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opAssignLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opAssignLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opAssignLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opAssignLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPut,
|
||||
"/repos/{repo_ref}/pullreq/{pullreq_number}/labels", opAssignLabel)
|
||||
|
||||
opListLabels := openapi3.Operation{}
|
||||
opListLabels.WithTags("pullreq")
|
||||
opListLabels.WithMapOfAnything(map[string]interface{}{"operationId": "listLabels"})
|
||||
opListLabels.WithParameters(
|
||||
QueryParameterPage, QueryParameterLimit, queryParameterAssignable, queryParameterQueryLabel)
|
||||
_ = reflector.SetRequest(&opListLabels, new(pullReqRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(types.ScopesLabels), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet,
|
||||
"/repos/{repo_ref}/pullreq/{pullreq_number}/labels", opListLabels)
|
||||
|
||||
opUnassignLabel := openapi3.Operation{}
|
||||
opUnassignLabel.WithTags("pullreq")
|
||||
opUnassignLabel.WithMapOfAnything(map[string]interface{}{"operationId": "unassignLabel"})
|
||||
_ = reflector.SetRequest(&opUnassignLabel, struct {
|
||||
pullReqRequest
|
||||
LabelID int64 `path:"label_id"`
|
||||
}{}, http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opUnassignLabel, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opUnassignLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUnassignLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUnassignLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUnassignLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodDelete,
|
||||
"/repos/{repo_ref}/pullreq/{pullreq_number}/labels/{label_id}", opUnassignLabel)
|
||||
}
|
||||
|
@ -221,6 +221,18 @@ type archiveRequest struct {
|
||||
Format string `path:"format" required:"true"`
|
||||
}
|
||||
|
||||
type labelRequest struct {
|
||||
Key string `json:"key"`
|
||||
Description string `json:"description"`
|
||||
Type enum.LabelType `json:"type"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
}
|
||||
|
||||
type labelValueRequest struct {
|
||||
Value string `json:"value"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
}
|
||||
|
||||
var queryParameterGitRef = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamGitRef,
|
||||
@ -603,6 +615,35 @@ var queryParamArchiveCompression = openapi3.ParameterOrRef{
|
||||
},
|
||||
}
|
||||
|
||||
var queryParameterInherited = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamInherited,
|
||||
In: openapi3.ParameterInQuery,
|
||||
Description: ptr.String("The result should inherit labels from parent parent spaces."),
|
||||
Required: ptr.Bool(false),
|
||||
Schema: &openapi3.SchemaOrRef{
|
||||
Schema: &openapi3.Schema{
|
||||
Type: ptrSchemaType(openapi3.SchemaTypeBoolean),
|
||||
Default: ptrptr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var queryParameterQueryLabel = openapi3.ParameterOrRef{
|
||||
Parameter: &openapi3.Parameter{
|
||||
Name: request.QueryParamQuery,
|
||||
In: openapi3.ParameterInQuery,
|
||||
Description: ptr.String("The substring which is used to filter the labels by their key."),
|
||||
Required: ptr.Bool(false),
|
||||
Schema: &openapi3.SchemaOrRef{
|
||||
Schema: &openapi3.Schema{
|
||||
Type: ptrSchemaType(openapi3.SchemaTypeString),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func repoOperations(reflector *openapi3.Reflector) {
|
||||
createRepository := openapi3.Operation{}
|
||||
@ -1168,4 +1209,157 @@ func repoOperations(reflector *openapi3.Reflector) {
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opSummary, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/summary", opSummary)
|
||||
|
||||
opDefineLabel := openapi3.Operation{}
|
||||
opDefineLabel.WithTags("repository")
|
||||
opDefineLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "defineRepoLabel"})
|
||||
_ = reflector.SetRequest(&opDefineLabel, &struct {
|
||||
repoRequest
|
||||
labelRequest
|
||||
}{}, http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(types.Label), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/labels", opDefineLabel)
|
||||
|
||||
opSaveLabel := openapi3.Operation{}
|
||||
opSaveLabel.WithTags("repository")
|
||||
opSaveLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "saveRepoLabel"})
|
||||
_ = reflector.SetRequest(&opSaveLabel, &struct {
|
||||
repoRequest
|
||||
types.SaveInput
|
||||
}{}, http.MethodPut)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(types.LabelWithValues), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPut, "/repos/{repo_ref}/labels", opSaveLabel)
|
||||
|
||||
opListLabels := openapi3.Operation{}
|
||||
opListLabels.WithTags("repository")
|
||||
opListLabels.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "listRepoLabels"})
|
||||
opListLabels.WithParameters(
|
||||
QueryParameterPage, QueryParameterLimit, queryParameterInherited, queryParameterQueryLabel)
|
||||
_ = reflector.SetRequest(&opListLabels, new(repoRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new([]*types.Label), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/labels", opListLabels)
|
||||
|
||||
opDeleteLabel := openapi3.Operation{}
|
||||
opDeleteLabel.WithTags("repository")
|
||||
opDeleteLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "deleteRepoLabel"})
|
||||
_ = reflector.SetRequest(&opDeleteLabel, &struct {
|
||||
repoRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(
|
||||
http.MethodDelete, "/repos/{repo_ref}/labels/{key}", opDeleteLabel)
|
||||
|
||||
opUpdateLabel := openapi3.Operation{}
|
||||
opUpdateLabel.WithTags("repository")
|
||||
opUpdateLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "updateRepoLabel"})
|
||||
_ = reflector.SetRequest(&opUpdateLabel, &struct {
|
||||
repoRequest
|
||||
labelRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodPatch)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(types.Label), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPatch, "/repos/{repo_ref}/labels/{key}", opUpdateLabel)
|
||||
|
||||
opDefineLabelValue := openapi3.Operation{}
|
||||
opDefineLabelValue.WithTags("repository")
|
||||
opDefineLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "defineRepoLabelValue"})
|
||||
_ = reflector.SetRequest(&opDefineLabelValue, &struct {
|
||||
repoRequest
|
||||
labelValueRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(types.LabelValue), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost,
|
||||
"/repos/{repo_ref}/labels/{key}/values", opDefineLabelValue)
|
||||
|
||||
opListLabelValues := openapi3.Operation{}
|
||||
opListLabelValues.WithTags("repository")
|
||||
opListLabelValues.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "listRepoLabelValues"})
|
||||
_ = reflector.SetRequest(&opListLabelValues, &struct {
|
||||
repoRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new([]*types.LabelValue), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet,
|
||||
"/repos/{repo_ref}/labels/{key}/values", opListLabelValues)
|
||||
|
||||
opDeleteLabelValue := openapi3.Operation{}
|
||||
opDeleteLabelValue.WithTags("repository")
|
||||
opDeleteLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "deleteRepoLabelValue"})
|
||||
_ = reflector.SetRequest(&opDeleteLabelValue, &struct {
|
||||
repoRequest
|
||||
Key string `path:"key"`
|
||||
Value string `path:"value"`
|
||||
}{}, http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(
|
||||
http.MethodDelete, "/repos/{repo_ref}/labels/{key}/values/{value}", opDeleteLabelValue)
|
||||
|
||||
opUpdateLabelValue := openapi3.Operation{}
|
||||
opUpdateLabelValue.WithTags("repository")
|
||||
opUpdateLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "updateRepoLabelValue"})
|
||||
_ = reflector.SetRequest(&opUpdateLabelValue, &struct {
|
||||
repoRequest
|
||||
labelValueRequest
|
||||
Key string `path:"key"`
|
||||
Value string `path:"value"`
|
||||
}{}, http.MethodPatch)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(types.LabelValue), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPatch,
|
||||
"/repos/{repo_ref}/labels/{key}/values/{value}", opUpdateLabelValue)
|
||||
}
|
||||
|
@ -446,4 +446,158 @@ func spaceOperations(reflector *openapi3.Reflector) {
|
||||
_ = reflector.SetJSONResponse(&opMembershipList, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opMembershipList, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/members", opMembershipList)
|
||||
|
||||
opDefineLabel := openapi3.Operation{}
|
||||
opDefineLabel.WithTags("space")
|
||||
opDefineLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "defineSpaceLabel"})
|
||||
_ = reflector.SetRequest(&opDefineLabel, &struct {
|
||||
spaceRequest
|
||||
labelRequest
|
||||
}{}, http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(types.Label), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost, "/spaces/{space_ref}/labels", opDefineLabel)
|
||||
|
||||
opSaveLabel := openapi3.Operation{}
|
||||
opSaveLabel.WithTags("space")
|
||||
opSaveLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "saveSpaceLabel"})
|
||||
_ = reflector.SetRequest(&opSaveLabel, &struct {
|
||||
spaceRequest
|
||||
types.SaveInput
|
||||
}{}, http.MethodPut)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(types.LabelWithValues), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opSaveLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPut, "/spaces/{space_ref}/labels", opSaveLabel)
|
||||
|
||||
opListLabels := openapi3.Operation{}
|
||||
opListLabels.WithTags("space")
|
||||
opListLabels.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "listSpaceLabels"})
|
||||
opListLabels.WithParameters(
|
||||
QueryParameterPage, QueryParameterLimit, queryParameterInherited, queryParameterQueryLabel)
|
||||
_ = reflector.SetRequest(&opListLabels, new(spaceRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new([]*types.Label), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opListLabels, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/labels", opListLabels)
|
||||
|
||||
opDeleteLabel := openapi3.Operation{}
|
||||
opDeleteLabel.WithTags("space")
|
||||
opDeleteLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "deleteSpaceLabel"})
|
||||
_ = reflector.SetRequest(&opDeleteLabel, &struct {
|
||||
spaceRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(
|
||||
http.MethodDelete, "/spaces/{space_ref}/labels/{key}", opDeleteLabel)
|
||||
|
||||
opUpdateLabel := openapi3.Operation{}
|
||||
opUpdateLabel.WithTags("space")
|
||||
opUpdateLabel.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "updateSpaceLabel"})
|
||||
_ = reflector.SetRequest(&opUpdateLabel, &struct {
|
||||
spaceRequest
|
||||
labelRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodPatch)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(types.Label), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabel, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPatch,
|
||||
"/spaces/{space_ref}/labels/{key}", opUpdateLabel)
|
||||
|
||||
opDefineLabelValue := openapi3.Operation{}
|
||||
opDefineLabelValue.WithTags("space")
|
||||
opDefineLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "defineSpaceLabelValue"})
|
||||
_ = reflector.SetRequest(&opDefineLabelValue, &struct {
|
||||
spaceRequest
|
||||
labelValueRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(types.LabelValue), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDefineLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost,
|
||||
"/spaces/{space_ref}/labels/{key}/values", opDefineLabelValue)
|
||||
|
||||
opListLabelValues := openapi3.Operation{}
|
||||
opListLabelValues.WithTags("space")
|
||||
opListLabelValues.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "listSpaceLabelValues"})
|
||||
_ = reflector.SetRequest(&opListLabelValues, &struct {
|
||||
spaceRequest
|
||||
Key string `path:"key"`
|
||||
}{}, http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new([]*types.LabelValue), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opListLabelValues, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet,
|
||||
"/spaces/{space_ref}/labels/{key}/values", opListLabelValues)
|
||||
|
||||
opDeleteLabelValue := openapi3.Operation{}
|
||||
opDeleteLabelValue.WithTags("space")
|
||||
opDeleteLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "deleteSpaceLabelValue"})
|
||||
_ = reflector.SetRequest(&opDeleteLabelValue, &struct {
|
||||
spaceRequest
|
||||
Key string `path:"key"`
|
||||
Value string `path:"value"`
|
||||
}{}, http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDeleteLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(
|
||||
http.MethodDelete, "/spaces/{space_ref}/labels/{key}/values/{value}", opDeleteLabelValue)
|
||||
|
||||
opUpdateLabelValue := openapi3.Operation{}
|
||||
opUpdateLabelValue.WithTags("space")
|
||||
opUpdateLabelValue.WithMapOfAnything(
|
||||
map[string]interface{}{"operationId": "updateSpaceLabelValue"})
|
||||
_ = reflector.SetRequest(&opUpdateLabelValue, &struct {
|
||||
spaceRequest
|
||||
labelValueRequest
|
||||
Key string `path:"key"`
|
||||
Value string `path:"value"`
|
||||
}{}, http.MethodPatch)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(types.LabelValue), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opUpdateLabelValue, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPatch,
|
||||
"/spaces/{space_ref}/labels/{key}/values/{value}", opUpdateLabelValue)
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ const (
|
||||
PerPageDefault = 30
|
||||
PerPageMax = 100
|
||||
|
||||
QueryParamInherited = "inherited"
|
||||
QueryParamAssignable = "assignable"
|
||||
|
||||
// TODO: have shared constants across all services?
|
||||
HeaderRequestID = "X-Request-Id"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
@ -155,6 +158,16 @@ func ParseRecursiveFromQuery(r *http.Request) (bool, error) {
|
||||
return QueryParamAsBoolOrDefault(r, QueryParamRecursive, false)
|
||||
}
|
||||
|
||||
// ParseInheritedFromQuery extracts the inherited option from the URL query.
|
||||
func ParseInheritedFromQuery(r *http.Request) (bool, error) {
|
||||
return QueryParamAsBoolOrDefault(r, QueryParamInherited, false)
|
||||
}
|
||||
|
||||
// ParseAssignableFromQuery extracts the assignable option from the URL query.
|
||||
func ParseAssignableFromQuery(r *http.Request) (bool, error) {
|
||||
return QueryParamAsBoolOrDefault(r, QueryParamAssignable, false)
|
||||
}
|
||||
|
||||
// GetDeletedAtFromQueryOrError gets the exact resource deletion timestamp from the query.
|
||||
func GetDeletedAtFromQueryOrError(r *http.Request) (int64, error) {
|
||||
return QueryParamAsPositiveInt64OrError(r, QueryParamDeletedAt)
|
||||
|
67
app/api/request/label.go
Normal file
67
app/api/request/label.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
const (
|
||||
PathParamLabelKey = "label_key"
|
||||
PathParamLabelValue = "label_value"
|
||||
PathParamLabelID = "label_id"
|
||||
)
|
||||
|
||||
func GetLabelKeyFromPath(r *http.Request) (string, error) {
|
||||
return EncodedPathParamOrError(r, PathParamLabelKey)
|
||||
}
|
||||
|
||||
func GetLabelValueFromPath(r *http.Request) (string, error) {
|
||||
return EncodedPathParamOrError(r, PathParamLabelValue)
|
||||
}
|
||||
|
||||
func GetLabelIDFromPath(r *http.Request) (int64, error) {
|
||||
return PathParamAsPositiveInt64(r, PathParamLabelID)
|
||||
}
|
||||
|
||||
// ParseLabelFilter extracts the label filter from the url.
|
||||
func ParseLabelFilter(r *http.Request) (*types.LabelFilter, error) {
|
||||
// inherited is used to list labels from parent scopes
|
||||
inherited, err := ParseInheritedFromQuery(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.LabelFilter{
|
||||
Inherited: inherited,
|
||||
ListQueryFilter: ParseListQueryFilterFromRequest(r),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseAssignableLabelFilter extracts the assignable label filter from the url.
|
||||
func ParseAssignableLabelFilter(r *http.Request) (*types.AssignableLabelFilter, error) {
|
||||
// assignable is used to list all labels assignable to pullreq
|
||||
assignable, err := ParseAssignableFromQuery(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AssignableLabelFilter{
|
||||
Assignable: assignable,
|
||||
ListQueryFilter: ParseListQueryFilterFromRequest(r),
|
||||
}, nil
|
||||
}
|
@ -18,6 +18,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
@ -72,6 +73,24 @@ func PathParamOrError(r *http.Request, paramName string) (string, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// EncodedPathParamOrError tries to retrieve the parameter from the request and
|
||||
// returns the parameter if it exists and is not empty, otherwise returns an error,
|
||||
// then it tries to URL decode parameter value,
|
||||
// and returns decoded value, or an error on decoding failure.
|
||||
func EncodedPathParamOrError(r *http.Request, paramName string) (string, error) {
|
||||
val, err := PathParamOrError(r, paramName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
decoded, err := url.PathUnescape(val)
|
||||
if err != nil {
|
||||
return "", usererror.BadRequestf("Value %s for param %s has incorrect encoding", val, paramName)
|
||||
}
|
||||
|
||||
return decoded, nil
|
||||
}
|
||||
|
||||
// PathParamOrEmpty retrieves the path parameter or returns an empty string otherwise.
|
||||
func PathParamOrEmpty(r *http.Request, paramName string) string {
|
||||
val, ok := PathParam(r, paramName)
|
||||
|
@ -91,8 +91,8 @@ func Translate(ctx context.Context, err error) *Error {
|
||||
log.Ctx(ctx).Warn().Err(appError.Err).Msgf("Application error translation is omitting internal details.")
|
||||
}
|
||||
|
||||
return NewWithPayload(httpStatusCode(
|
||||
appError.Status),
|
||||
return NewWithPayload(
|
||||
httpStatusCode(appError.Status),
|
||||
appError.Message,
|
||||
appError.Details,
|
||||
)
|
||||
|
@ -227,7 +227,12 @@ func setupRoutesV1WithAuth(r chi.Router,
|
||||
}
|
||||
|
||||
// nolint: revive // it's the app context, it shouldn't be the first argument
|
||||
func setupSpaces(r chi.Router, appCtx context.Context, spaceCtrl *space.Controller) {
|
||||
func setupSpaces(
|
||||
r chi.Router,
|
||||
appCtx context.Context,
|
||||
spaceCtrl *space.Controller,
|
||||
|
||||
) {
|
||||
r.Route("/spaces", func(r chi.Router) {
|
||||
// Create takes path and parentId via body, not uri
|
||||
r.Post("/", handlerspace.HandleCreate(spaceCtrl))
|
||||
@ -264,6 +269,26 @@ func setupSpaces(r chi.Router, appCtx context.Context, spaceCtrl *space.Controll
|
||||
r.Patch("/", handlerspace.HandleMembershipUpdate(spaceCtrl))
|
||||
})
|
||||
})
|
||||
|
||||
r.Route("/labels", func(r chi.Router) {
|
||||
r.Post("/", handlerspace.HandleDefineLabel(spaceCtrl))
|
||||
r.Get("/", handlerspace.HandleListLabels(spaceCtrl))
|
||||
r.Put("/", handlerspace.HandleSaveLabel(spaceCtrl))
|
||||
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamLabelKey), func(r chi.Router) {
|
||||
r.Delete("/", handlerspace.HandleDeleteLabel(spaceCtrl))
|
||||
r.Patch("/", handlerspace.HandleUpdateLabel(spaceCtrl))
|
||||
|
||||
r.Route("/values", func(r chi.Router) {
|
||||
r.Post("/", handlerspace.HandleDefineLabelValue(spaceCtrl))
|
||||
r.Get("/", handlerspace.HandleListLabelValues(spaceCtrl))
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamLabelValue), func(r chi.Router) {
|
||||
r.Delete("/", handlerspace.HandleDeleteLabelValue(spaceCtrl))
|
||||
r.Patch("/", handlerspace.HandleUpdateLabelValue(spaceCtrl))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -385,6 +410,26 @@ func setupRepos(r chi.Router,
|
||||
SetupUploads(r, uploadCtrl)
|
||||
|
||||
SetupRules(r, repoCtrl)
|
||||
|
||||
r.Route("/labels", func(r chi.Router) {
|
||||
r.Post("/", handlerrepo.HandleDefineLabel(repoCtrl))
|
||||
r.Get("/", handlerrepo.HandleListLabels(repoCtrl))
|
||||
r.Put("/", handlerrepo.HandleSaveLabel(repoCtrl))
|
||||
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamLabelKey), func(r chi.Router) {
|
||||
r.Delete("/", handlerrepo.HandleDeleteLabel(repoCtrl))
|
||||
r.Patch("/", handlerrepo.HandleUpdateLabel(repoCtrl))
|
||||
|
||||
r.Route("/values", func(r chi.Router) {
|
||||
r.Post("/", handlerrepo.HandleDefineLabelValue(repoCtrl))
|
||||
r.Get("/", handlerrepo.HandleListLabelValues(repoCtrl))
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamLabelValue), func(r chi.Router) {
|
||||
r.Delete("/", handlerrepo.HandleDeleteLabelValue(repoCtrl))
|
||||
r.Patch("/", handlerrepo.HandleUpdateLabelValue(repoCtrl))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -565,6 +610,14 @@ func SetupPullReq(r chi.Router, pullreqCtrl *pullreq.Controller) {
|
||||
r.Get("/diff", handlerpullreq.HandleDiff(pullreqCtrl))
|
||||
r.Post("/diff", handlerpullreq.HandleDiff(pullreqCtrl))
|
||||
r.Get("/checks", handlerpullreq.HandleCheckList(pullreqCtrl))
|
||||
|
||||
r.Route("/labels", func(r chi.Router) {
|
||||
r.Put("/", handlerpullreq.HandleAssignLabel(pullreqCtrl))
|
||||
r.Get("/", handlerpullreq.HandleListLabels(pullreqCtrl))
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamLabelID), func(r chi.Router) {
|
||||
r.Delete("/", handlerpullreq.HandleUnassignLabel(pullreqCtrl))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
305
app/services/label/label.go
Normal file
305
app/services/label/label.go
Normal file
@ -0,0 +1,305 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func (s *Service) Define(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
spaceID, repoID *int64,
|
||||
in *types.DefineLabelInput,
|
||||
) (*types.Label, error) {
|
||||
var scope int64
|
||||
if spaceID != nil {
|
||||
spaceIDs, err := s.spaceStore.GetAncestorIDs(ctx, *spaceID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get space ids hierarchy: %w", err)
|
||||
}
|
||||
scope = int64(len(spaceIDs))
|
||||
}
|
||||
|
||||
label := newLabel(principalID, spaceID, repoID, scope, in)
|
||||
|
||||
if err := s.labelStore.Define(ctx, label); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
||||
|
||||
func (s *Service) Update(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
spaceID, repoID *int64,
|
||||
key string,
|
||||
in *types.UpdateLabelInput,
|
||||
) (*types.Label, error) {
|
||||
label, err := s.labelStore.Find(ctx, spaceID, repoID, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo label: %w", err)
|
||||
}
|
||||
|
||||
return s.update(ctx, principalID, label, in)
|
||||
}
|
||||
|
||||
func (s *Service) update(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
label *types.Label,
|
||||
in *types.UpdateLabelInput,
|
||||
) (*types.Label, error) {
|
||||
label, hasChanges := applyChanges(principalID, label, in)
|
||||
if !hasChanges {
|
||||
return label, nil
|
||||
}
|
||||
|
||||
err := s.labelStore.Update(ctx, label)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update label: %w", err)
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func (s *Service) Save(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
spaceID, repoID *int64,
|
||||
in *types.SaveInput,
|
||||
) (*types.LabelWithValues, error) {
|
||||
var label *types.Label
|
||||
var valuesToReturn []*types.LabelValue
|
||||
var err error
|
||||
|
||||
err = s.tx.WithTx(ctx, func(ctx context.Context) error {
|
||||
label, err = s.labelStore.FindByID(ctx, in.Label.ID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, store.ErrResourceNotFound) {
|
||||
return err
|
||||
}
|
||||
label, err = s.Define(ctx, principalID, spaceID, repoID, &in.Label.DefineLabelInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
label, err = s.update(ctx, principalID, label, &types.UpdateLabelInput{
|
||||
Key: &in.Label.Key,
|
||||
Type: &label.Type,
|
||||
Description: &label.Description,
|
||||
Color: &in.Label.Color,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
existingValues, err := s.labelValueStore.List(ctx, label.ID, &types.ListQueryFilter{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existingValuesMap := make(map[int64]*types.LabelValue, len(existingValues))
|
||||
for _, value := range existingValues {
|
||||
existingValuesMap[value.ID] = value
|
||||
}
|
||||
|
||||
var valuesToCreate []*types.SaveLabelValueInput
|
||||
valuesToUpdate := make(map[int64]*types.SaveLabelValueInput)
|
||||
var valuesToDelete []string
|
||||
|
||||
for _, value := range in.Values {
|
||||
if _, ok := existingValuesMap[value.ID]; ok {
|
||||
valuesToUpdate[value.ID] = value
|
||||
} else {
|
||||
valuesToCreate = append(valuesToCreate, value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, value := range existingValues {
|
||||
if _, ok := valuesToUpdate[value.ID]; !ok {
|
||||
valuesToDelete = append(valuesToDelete, value.Value)
|
||||
}
|
||||
}
|
||||
|
||||
valuesToReturn = make([]*types.LabelValue, len(valuesToCreate)+len(valuesToUpdate))
|
||||
|
||||
for i, value := range valuesToCreate {
|
||||
valuesToReturn[i] = newLabelValue(principalID, label.ID, &value.DefineValueInput)
|
||||
if err = s.labelValueStore.Define(ctx, valuesToReturn[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
i := len(valuesToCreate)
|
||||
for _, value := range valuesToUpdate {
|
||||
if valuesToReturn[i], err = s.updateValue(ctx, principalID, existingValuesMap[value.ID], &types.UpdateValueInput{
|
||||
Value: &value.Value,
|
||||
Color: &value.Color,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if err = s.labelValueStore.DeleteMany(ctx, label.ID, valuesToDelete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if label.ValueCount, err = s.labelStore.IncrementValueCount(
|
||||
ctx, label.ID, len(valuesToCreate)-len(valuesToDelete)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(valuesToReturn, func(i, j int) bool {
|
||||
return valuesToReturn[i].Value < valuesToReturn[j].Value
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save label: %w", err)
|
||||
}
|
||||
|
||||
return &types.LabelWithValues{
|
||||
Label: *label,
|
||||
Values: valuesToReturn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Find(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
key string,
|
||||
) (*types.Label, error) {
|
||||
return s.labelStore.Find(ctx, spaceID, repoID, key)
|
||||
}
|
||||
|
||||
func (s *Service) List(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
if filter.Inherited {
|
||||
return s.listInScopes(ctx, spaceID, repoID, filter)
|
||||
}
|
||||
|
||||
return s.list(ctx, spaceID, repoID, filter)
|
||||
}
|
||||
|
||||
func (s *Service) list(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
if repoID != nil {
|
||||
return s.labelStore.List(ctx, nil, repoID, filter)
|
||||
}
|
||||
return s.labelStore.List(ctx, spaceID, nil, filter)
|
||||
}
|
||||
|
||||
func (s *Service) listInScopes(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
var spaceIDs []int64
|
||||
var repoIDVal int64
|
||||
var err error
|
||||
if repoID != nil {
|
||||
spaceIDs, err = s.spaceStore.GetAncestorIDs(ctx, *spaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoIDVal = *repoID
|
||||
} else {
|
||||
spaceIDs, err = s.spaceStore.GetAncestorIDs(ctx, *spaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.labelStore.ListInScopes(ctx, repoIDVal, spaceIDs, filter)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
key string,
|
||||
) error {
|
||||
return s.labelStore.Delete(ctx, spaceID, repoID, key)
|
||||
}
|
||||
|
||||
func newLabel(
|
||||
principalID int64,
|
||||
spaceID, repoID *int64,
|
||||
scope int64,
|
||||
in *types.DefineLabelInput,
|
||||
) *types.Label {
|
||||
now := time.Now().UnixMilli()
|
||||
return &types.Label{
|
||||
RepoID: repoID,
|
||||
SpaceID: spaceID,
|
||||
Scope: scope,
|
||||
Key: in.Key,
|
||||
Type: in.Type,
|
||||
Description: in.Description,
|
||||
Color: in.Color,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
CreatedBy: principalID,
|
||||
UpdatedBy: principalID,
|
||||
}
|
||||
}
|
||||
|
||||
func applyChanges(principalID int64, label *types.Label, in *types.UpdateLabelInput) (*types.Label, bool) {
|
||||
hasChanges := false
|
||||
|
||||
if label.UpdatedBy != principalID {
|
||||
hasChanges = true
|
||||
label.UpdatedBy = principalID
|
||||
}
|
||||
if in.Key != nil && label.Key != *in.Key {
|
||||
hasChanges = true
|
||||
label.Key = *in.Key
|
||||
}
|
||||
if in.Description != nil && label.Description != *in.Description {
|
||||
hasChanges = true
|
||||
label.Description = *in.Description
|
||||
}
|
||||
if in.Color != nil && label.Color != *in.Color {
|
||||
hasChanges = true
|
||||
label.Color = *in.Color
|
||||
}
|
||||
if in.Type != nil && label.Type != *in.Type {
|
||||
hasChanges = true
|
||||
label.Type = *in.Type
|
||||
}
|
||||
|
||||
if hasChanges {
|
||||
label.Updated = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
return label, hasChanges
|
||||
}
|
274
app/services/label/label_pullreq.go
Normal file
274
app/services/label/label_pullreq.go
Normal file
@ -0,0 +1,274 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func (s *Service) AssignToPullReq(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
pullreqID int64,
|
||||
repoID int64,
|
||||
repoParentID int64,
|
||||
in *types.PullReqCreateInput,
|
||||
) (*types.PullReqLabel, error) {
|
||||
label, err := s.labelStore.FindByID(ctx, in.LabelID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find label by id: %w", err)
|
||||
}
|
||||
|
||||
if label.SpaceID != nil {
|
||||
spaceIDs, err := s.spaceStore.GetAncestorIDs(ctx, repoParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent space ids: %w", err)
|
||||
}
|
||||
if ok := slices.Contains(spaceIDs, *label.SpaceID); !ok {
|
||||
return nil, errors.NotFound(
|
||||
"label %d is not defined in current space tree path", label.ID)
|
||||
}
|
||||
} else if label.RepoID != nil && *label.RepoID != repoID {
|
||||
return nil, errors.InvalidArgument(
|
||||
"label %d is not defined in current repo", label.ID)
|
||||
}
|
||||
|
||||
pullreqLabel := newPullReqLabel(pullreqID, principalID, in)
|
||||
|
||||
if in.ValueID != nil {
|
||||
labelValue, err := s.labelValueStore.FindByID(ctx, *in.ValueID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find label value by id: %w", err)
|
||||
}
|
||||
if label.ID != labelValue.LabelID {
|
||||
return nil, errors.InvalidArgument("label value is not associated with label")
|
||||
}
|
||||
}
|
||||
|
||||
if in.Value != "" {
|
||||
valueID, err := s.getOrDefineValue(ctx, principalID, label, in.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullreqLabel.ValueID = &valueID
|
||||
}
|
||||
|
||||
err = s.pullReqLabelAssignmentStore.Assign(ctx, pullreqLabel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to assign label to pullreq: %w", err)
|
||||
}
|
||||
|
||||
return pullreqLabel, nil
|
||||
}
|
||||
|
||||
func (s *Service) getOrDefineValue(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
label *types.Label,
|
||||
value string,
|
||||
) (int64, error) {
|
||||
if label.Type != enum.LabelTypeDynamic {
|
||||
return 0, errors.InvalidArgument("label doesn't allow new value assignment")
|
||||
}
|
||||
|
||||
labelValue, err := s.labelValueStore.FindByLabelID(ctx, label.ID, value)
|
||||
if err == nil {
|
||||
return labelValue.ID, nil
|
||||
}
|
||||
if !errors.Is(err, store.ErrResourceNotFound) {
|
||||
return 0, fmt.Errorf("failed to find label value: %w", err)
|
||||
}
|
||||
|
||||
labelValue, err = s.DefineValue(
|
||||
ctx,
|
||||
principalID,
|
||||
label.ID,
|
||||
&types.DefineValueInput{
|
||||
Value: value,
|
||||
Color: label.Color,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create label value: %w", err)
|
||||
}
|
||||
|
||||
return labelValue.ID, nil
|
||||
}
|
||||
|
||||
func (s *Service) UnassignFromPullReq(
|
||||
ctx context.Context, repoID, repoParentID, pullreqID, labelID int64,
|
||||
) error {
|
||||
label, err := s.labelStore.FindByID(ctx, labelID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find label by id: %w", err)
|
||||
}
|
||||
|
||||
if label.RepoID != nil && *label.RepoID != repoID {
|
||||
return errors.InvalidArgument(
|
||||
"label %d is not defined in current repo", label.ID)
|
||||
} else if label.SpaceID != nil {
|
||||
spaceIDs, err := s.spaceStore.GetAncestorIDs(ctx, repoParentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get parent space ids: %w", err)
|
||||
}
|
||||
if ok := slices.Contains(spaceIDs, *label.SpaceID); !ok {
|
||||
return errors.NotFound(
|
||||
"label %d is not defined in current space tree path", label.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return s.pullReqLabelAssignmentStore.Unassign(ctx, pullreqID, labelID)
|
||||
}
|
||||
|
||||
func (s *Service) ListPullReqLabels(
|
||||
ctx context.Context,
|
||||
repo *types.Repository,
|
||||
spaceID int64,
|
||||
pullreqID int64,
|
||||
filter *types.AssignableLabelFilter,
|
||||
) (*types.ScopesLabels, error) {
|
||||
spaces, err := s.spaceStore.GetHierarchy(ctx, spaceID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get space hierarchy: %w", err)
|
||||
}
|
||||
|
||||
spaceIDs := make([]int64, len(spaces))
|
||||
for i, space := range spaces {
|
||||
spaceIDs[i] = space.ID
|
||||
}
|
||||
|
||||
scopeLabelsMap := make(map[int64]*types.ScopeData)
|
||||
|
||||
pullreqAssignments, err := s.pullReqLabelAssignmentStore.ListAssigned(
|
||||
ctx, pullreqID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list labels assigned to pullreq: %w", err)
|
||||
}
|
||||
|
||||
if !filter.Assignable {
|
||||
sortedAssignments := maps.Values(pullreqAssignments)
|
||||
sort.Slice(sortedAssignments, func(i, j int) bool {
|
||||
if sortedAssignments[i].Key != sortedAssignments[j].Key {
|
||||
return sortedAssignments[i].Key < sortedAssignments[j].Key
|
||||
}
|
||||
return sortedAssignments[i].Scope < sortedAssignments[j].Scope
|
||||
})
|
||||
|
||||
populateScopeLabelsMap(sortedAssignments, scopeLabelsMap, repo, spaces)
|
||||
return createScopeLabels(sortedAssignments, scopeLabelsMap), nil
|
||||
}
|
||||
|
||||
labelInfos, err := s.labelStore.ListInfosInScopes(ctx, repo.ID, spaceIDs, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repo and spaces label infos: %w", err)
|
||||
}
|
||||
|
||||
labelIDs := make([]int64, len(labelInfos))
|
||||
for i, labelInfo := range labelInfos {
|
||||
labelIDs[i] = labelInfo.ID
|
||||
}
|
||||
|
||||
valueInfos, err := s.labelValueStore.ListInfosByLabelIDs(ctx, labelIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list label value infos by label ids: %w", err)
|
||||
}
|
||||
|
||||
allAssignments := make([]*types.LabelAssignment, len(labelInfos))
|
||||
for i, labelInfo := range labelInfos {
|
||||
assignment, ok := pullreqAssignments[labelInfo.ID]
|
||||
if !ok {
|
||||
assignment = &types.LabelAssignment{
|
||||
LabelInfo: *labelInfo,
|
||||
}
|
||||
}
|
||||
assignment.LabelInfo.Assigned = &ok
|
||||
allAssignments[i] = assignment
|
||||
allAssignments[i].Values = valueInfos[labelInfo.ID]
|
||||
}
|
||||
|
||||
populateScopeLabelsMap(allAssignments, scopeLabelsMap, repo, spaces)
|
||||
return createScopeLabels(allAssignments, scopeLabelsMap), nil
|
||||
}
|
||||
|
||||
func populateScopeLabelsMap(
|
||||
assignments []*types.LabelAssignment,
|
||||
scopeLabelsMap map[int64]*types.ScopeData,
|
||||
repo *types.Repository,
|
||||
spaces []*types.Space,
|
||||
) {
|
||||
for _, assignment := range assignments {
|
||||
_, ok := scopeLabelsMap[assignment.Scope]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
scopeLabelsMap[assignment.Scope] = &types.ScopeData{Scope: assignment.Scope}
|
||||
if assignment.Scope == 0 {
|
||||
scopeLabelsMap[assignment.Scope].Repo = repo
|
||||
} else {
|
||||
for _, space := range spaces {
|
||||
if space.ID == *assignment.SpaceID {
|
||||
scopeLabelsMap[assignment.Scope].Space = space
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createScopeLabels(
|
||||
assignments []*types.LabelAssignment,
|
||||
scopeLabelsMap map[int64]*types.ScopeData,
|
||||
) *types.ScopesLabels {
|
||||
scopeData := make([]*types.ScopeData, len(scopeLabelsMap))
|
||||
for i, scopeLabel := range maps.Values(scopeLabelsMap) {
|
||||
scopeData[i] = scopeLabel
|
||||
}
|
||||
|
||||
sort.Slice(scopeData, func(i, j int) bool {
|
||||
return scopeData[i].Scope < scopeData[j].Scope
|
||||
})
|
||||
|
||||
return &types.ScopesLabels{
|
||||
LabelData: assignments,
|
||||
ScopeData: scopeData,
|
||||
}
|
||||
}
|
||||
|
||||
func newPullReqLabel(
|
||||
pullreqID int64,
|
||||
principalID int64,
|
||||
in *types.PullReqCreateInput,
|
||||
) *types.PullReqLabel {
|
||||
now := time.Now().UnixMilli()
|
||||
return &types.PullReqLabel{
|
||||
PullReqID: pullreqID,
|
||||
LabelID: in.LabelID,
|
||||
ValueID: in.ValueID,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
CreatedBy: principalID,
|
||||
UpdatedBy: principalID,
|
||||
}
|
||||
}
|
167
app/services/label/label_value.go
Normal file
167
app/services/label/label_value.go
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func (s *Service) DefineValue(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
labelID int64,
|
||||
in *types.DefineValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
labelValue := newLabelValue(principalID, labelID, in)
|
||||
|
||||
err := s.tx.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := s.labelValueStore.Define(ctx, labelValue); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.labelStore.IncrementValueCount(ctx, labelID, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return labelValue, nil
|
||||
}
|
||||
|
||||
func applyValueChanges(
|
||||
principalID int64,
|
||||
value *types.LabelValue,
|
||||
in *types.UpdateValueInput,
|
||||
) (*types.LabelValue, bool) {
|
||||
hasChanges := false
|
||||
|
||||
if value.UpdatedBy != principalID {
|
||||
hasChanges = true
|
||||
value.UpdatedBy = principalID
|
||||
}
|
||||
|
||||
if in.Value != nil && value.Value != *in.Value {
|
||||
hasChanges = true
|
||||
value.Value = *in.Value
|
||||
}
|
||||
if in.Color != nil && value.Color != *in.Color {
|
||||
hasChanges = true
|
||||
value.Color = *in.Color
|
||||
}
|
||||
|
||||
if hasChanges {
|
||||
value.Updated = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
return value, hasChanges
|
||||
}
|
||||
|
||||
func (s *Service) UpdateValue(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
labelID int64,
|
||||
value string,
|
||||
in *types.UpdateValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
labelValue, err := s.labelValueStore.FindByLabelID(ctx, labelID, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find label value: %w", err)
|
||||
}
|
||||
|
||||
return s.updateValue(ctx, principalID, labelValue, in)
|
||||
}
|
||||
|
||||
func (s *Service) updateValue(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
labelValue *types.LabelValue,
|
||||
in *types.UpdateValueInput,
|
||||
) (*types.LabelValue, error) {
|
||||
labelValue, hasChanges := applyValueChanges(
|
||||
principalID, labelValue, in)
|
||||
if !hasChanges {
|
||||
return labelValue, nil
|
||||
}
|
||||
|
||||
if err := s.labelValueStore.Update(ctx, labelValue); err != nil {
|
||||
return nil, fmt.Errorf("failed to update label value: %w", err)
|
||||
}
|
||||
|
||||
return labelValue, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListValues(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
labelKey string,
|
||||
filter *types.ListQueryFilter,
|
||||
) ([]*types.LabelValue, error) {
|
||||
label, err := s.labelStore.Find(ctx, spaceID, repoID, labelKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.labelValueStore.List(ctx, label.ID, filter)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteValue(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
labelKey string,
|
||||
value string,
|
||||
) error {
|
||||
label, err := s.labelStore.Find(ctx, spaceID, repoID, labelKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.tx.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := s.labelValueStore.Delete(ctx, label.ID, value); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.labelStore.IncrementValueCount(ctx, label.ID, -1); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newLabelValue(
|
||||
principalID int64,
|
||||
labelID int64,
|
||||
in *types.DefineValueInput,
|
||||
) *types.LabelValue {
|
||||
now := time.Now().UnixMilli()
|
||||
return &types.LabelValue{
|
||||
LabelID: labelID,
|
||||
Value: in.Value,
|
||||
Color: in.Color,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
CreatedBy: principalID,
|
||||
UpdatedBy: principalID,
|
||||
}
|
||||
}
|
44
app/services/label/service.go
Normal file
44
app/services/label/service.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
tx dbtx.Transactor
|
||||
spaceStore store.SpaceStore
|
||||
labelStore store.LabelStore
|
||||
labelValueStore store.LabelValueStore
|
||||
pullReqLabelAssignmentStore store.PullReqLabelAssignmentStore
|
||||
}
|
||||
|
||||
func New(
|
||||
tx dbtx.Transactor,
|
||||
spaceStore store.SpaceStore,
|
||||
labelStore store.LabelStore,
|
||||
labelValueStore store.LabelValueStore,
|
||||
pullReqLabelAssignmentStore store.PullReqLabelAssignmentStore,
|
||||
) *Service {
|
||||
return &Service{
|
||||
tx: tx,
|
||||
spaceStore: spaceStore,
|
||||
labelStore: labelStore,
|
||||
labelValueStore: labelValueStore,
|
||||
pullReqLabelAssignmentStore: pullReqLabelAssignmentStore,
|
||||
}
|
||||
}
|
36
app/services/label/wire.go
Normal file
36
app/services/label/wire.go
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 (
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideLabel,
|
||||
)
|
||||
|
||||
func ProvideLabel(
|
||||
tx dbtx.Transactor,
|
||||
spaceStore store.SpaceStore,
|
||||
labelStore store.LabelStore,
|
||||
labelValueStore store.LabelValueStore,
|
||||
pullReqLabelStore store.PullReqLabelAssignmentStore,
|
||||
) *Service {
|
||||
return New(tx, spaceStore, labelStore, labelValueStore, pullReqLabelStore)
|
||||
}
|
@ -166,6 +166,14 @@ type (
|
||||
// GetRootSpace returns a space where space_parent_id is NULL.
|
||||
GetRootSpace(ctx context.Context, spaceID int64) (*types.Space, error)
|
||||
|
||||
// GetAncestorIDs returns a list of all space IDs along the recursive path to the root space.
|
||||
GetAncestorIDs(ctx context.Context, spaceID int64) ([]int64, error)
|
||||
|
||||
GetHierarchy(
|
||||
ctx context.Context,
|
||||
spaceID int64,
|
||||
) ([]*types.Space, error)
|
||||
|
||||
// Create creates a new space
|
||||
Create(ctx context.Context, space *types.Space) error
|
||||
|
||||
@ -930,4 +938,108 @@ type (
|
||||
gitspaceConfigID int64,
|
||||
) (*types.GitspaceEvent, error)
|
||||
}
|
||||
|
||||
LabelStore interface {
|
||||
// Define defines a label.
|
||||
Define(ctx context.Context, lbl *types.Label) error
|
||||
|
||||
// Update updates a label.
|
||||
Update(ctx context.Context, lbl *types.Label) error
|
||||
|
||||
// Find finds a label defined in a specified space/repo with a specified key.
|
||||
Find(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
key string,
|
||||
) (*types.Label, error)
|
||||
|
||||
// Delete deletes a label defined in a specified space/repo with a specified key.
|
||||
Delete(ctx context.Context, spaceID, repoID *int64, key string) error
|
||||
|
||||
// List list labels defined in a specified space/repo.
|
||||
List(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error)
|
||||
|
||||
// FindByID finds label with a specified id.
|
||||
FindByID(ctx context.Context, id int64) (*types.Label, error)
|
||||
|
||||
// ListInScopes lists labels defined in repo and/or specified spaces.
|
||||
ListInScopes(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
scopeIDs []int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error)
|
||||
|
||||
// ListInfosInScopes lists label infos defined in repo and/or specified spaces.
|
||||
ListInfosInScopes(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
spaceIDs []int64,
|
||||
filter *types.AssignableLabelFilter,
|
||||
) ([]*types.LabelInfo, error)
|
||||
|
||||
// IncrementValueCount increments count of values defined for a specified label.
|
||||
IncrementValueCount(ctx context.Context, labelID int64, increment int) (int64, error)
|
||||
}
|
||||
|
||||
LabelValueStore interface {
|
||||
// Define defines a label value.
|
||||
Define(ctx context.Context, lbl *types.LabelValue) error
|
||||
|
||||
// Update updates a label value.
|
||||
Update(ctx context.Context, lblVal *types.LabelValue) error
|
||||
|
||||
// Delete deletes a label value associated with a specified label.
|
||||
Delete(ctx context.Context, labelID int64, value string) error
|
||||
|
||||
// Delete deletes specified label values associated with a specified label.
|
||||
DeleteMany(ctx context.Context, labelID int64, values []string) error
|
||||
|
||||
// FindByLabelID finds a label value defined for a specified label.
|
||||
FindByLabelID(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
value string,
|
||||
) (*types.LabelValue, error)
|
||||
|
||||
// List lists label values defined for a specified label.
|
||||
List(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
opts *types.ListQueryFilter,
|
||||
) ([]*types.LabelValue, error)
|
||||
|
||||
// FindByID finds label value with a specified id.
|
||||
FindByID(ctx context.Context, id int64) (*types.LabelValue, error)
|
||||
|
||||
// ListInfosByLabelIDs list label infos by a specified label id.
|
||||
ListInfosByLabelIDs(
|
||||
ctx context.Context,
|
||||
labelIDs []int64,
|
||||
) (map[int64][]*types.LabelValueInfo, error)
|
||||
|
||||
Upsert(ctx context.Context, lblVal *types.LabelValue) error
|
||||
}
|
||||
|
||||
PullReqLabelAssignmentStore interface {
|
||||
// Assign assigns a label to a pullreq.
|
||||
Assign(ctx context.Context, label *types.PullReqLabel) error
|
||||
|
||||
// Unassign removes a label from a pullreq with a specified id.
|
||||
Unassign(ctx context.Context, pullreqID int64, labelID int64) error
|
||||
|
||||
// ListAssigned list labels assigned to a specified pullreq.
|
||||
ListAssigned(ctx context.Context, pullreqID int64) (map[int64]*types.LabelAssignment, error)
|
||||
|
||||
// Find finds a label assigned to a pullreq with a specified id.
|
||||
FindByLabelID(
|
||||
ctx context.Context,
|
||||
pullreqID int64,
|
||||
labelID int64,
|
||||
) (*types.PullReqLabel, error)
|
||||
}
|
||||
)
|
||||
|
398
app/store/database/label.go
Normal file
398
app/store/database/label.go
Normal file
@ -0,0 +1,398 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
labelColumns = `
|
||||
label_space_id
|
||||
,label_repo_id
|
||||
,label_scope
|
||||
,label_key
|
||||
,label_description
|
||||
,label_type
|
||||
,label_color
|
||||
,label_created
|
||||
,label_updated
|
||||
,label_created_by
|
||||
,label_updated_by`
|
||||
|
||||
labelSelectBase = `SELECT label_id, ` + labelColumns + ` FROM labels`
|
||||
)
|
||||
|
||||
type label struct {
|
||||
ID int64 `db:"label_id"`
|
||||
SpaceID null.Int `db:"label_space_id"`
|
||||
RepoID null.Int `db:"label_repo_id"`
|
||||
Scope int64 `db:"label_scope"`
|
||||
Key string `db:"label_key"`
|
||||
Description string `db:"label_description"`
|
||||
Type enum.LabelType `db:"label_type"`
|
||||
Color enum.LabelColor `db:"label_color"`
|
||||
ValueCount int64 `db:"label_value_count"`
|
||||
Created int64 `db:"label_created"`
|
||||
Updated int64 `db:"label_updated"`
|
||||
CreatedBy int64 `db:"label_created_by"`
|
||||
UpdatedBy int64 `db:"label_updated_by"`
|
||||
}
|
||||
|
||||
type labelInfo struct {
|
||||
LabelID int64 `db:"label_id"`
|
||||
SpaceID null.Int `db:"label_space_id"`
|
||||
RepoID null.Int `db:"label_repo_id"`
|
||||
Scope int64 `db:"label_scope"`
|
||||
Key string `db:"label_key"`
|
||||
Type enum.LabelType `db:"label_type"`
|
||||
LabelColor enum.LabelColor `db:"label_color"`
|
||||
}
|
||||
|
||||
type labelStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func NewLabelStore(
|
||||
db *sqlx.DB,
|
||||
) store.LabelStore {
|
||||
return &labelStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
var _ store.LabelStore = (*labelStore)(nil)
|
||||
|
||||
func (s *labelStore) Define(ctx context.Context, lbl *types.Label) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO labels (` + labelColumns + `)` + `
|
||||
values (
|
||||
:label_space_id
|
||||
,:label_repo_id
|
||||
,:label_scope
|
||||
,:label_key
|
||||
,:label_description
|
||||
,:label_type
|
||||
,:label_color
|
||||
,:label_created
|
||||
,:label_updated
|
||||
,:label_created_by
|
||||
,:label_updated_by
|
||||
)
|
||||
RETURNING label_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalLabel(lbl))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, args...).Scan(&lbl.ID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to create label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelStore) Update(ctx context.Context, lbl *types.Label) error {
|
||||
const sqlQuery = `
|
||||
UPDATE labels SET
|
||||
label_key = :label_key
|
||||
,label_description = :label_description
|
||||
,label_type = :label_type
|
||||
,label_color = :label_color
|
||||
,label_updated = :label_updated
|
||||
,label_updated_by = :label_updated_by
|
||||
WHERE label_id = :label_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalLabel(lbl))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
||||
}
|
||||
|
||||
if _, err := db.ExecContext(ctx, query, args...); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to update label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelStore) IncrementValueCount(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
increment int,
|
||||
) (int64, error) {
|
||||
const sqlQuery = `
|
||||
UPDATE labels
|
||||
SET label_value_count = label_value_count + $1
|
||||
WHERE label_id = $2
|
||||
RETURNING label_value_count
|
||||
`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var valueCount int64
|
||||
if err := db.QueryRowContext(ctx, sqlQuery, increment, labelID).Scan(&valueCount); err != nil {
|
||||
return 0, database.ProcessSQLErrorf(ctx, err, "Failed to increment label_value_count")
|
||||
}
|
||||
|
||||
return valueCount, nil
|
||||
}
|
||||
|
||||
func (s *labelStore) Find(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
key string,
|
||||
) (*types.Label, error) {
|
||||
const sqlQuery = labelSelectBase + `
|
||||
WHERE (label_space_id = $1 OR label_repo_id = $2) AND LOWER(label_key) = LOWER($3)`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst label
|
||||
if err := db.GetContext(ctx, &dst, sqlQuery, spaceID, repoID, key); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
||||
}
|
||||
|
||||
return mapLabel(&dst), nil
|
||||
}
|
||||
|
||||
func (s *labelStore) FindByID(ctx context.Context, id int64) (*types.Label, error) {
|
||||
const sqlQuery = labelSelectBase + `
|
||||
WHERE label_id = $1`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst label
|
||||
if err := db.GetContext(ctx, &dst, sqlQuery, id); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
||||
}
|
||||
|
||||
return mapLabel(&dst), nil
|
||||
}
|
||||
|
||||
func (s *labelStore) Delete(ctx context.Context, spaceID, repoID *int64, name string) error {
|
||||
const sqlQuery = `
|
||||
DELETE FROM labels
|
||||
WHERE (label_space_id = $1 OR label_repo_id = $2) AND LOWER(label_key) = LOWER($3)`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, sqlQuery, spaceID, repoID, name); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to delete label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns a list of pull requests for a repo or space.
|
||||
func (s *labelStore) List(
|
||||
ctx context.Context,
|
||||
spaceID, repoID *int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
stmt := database.Builder.
|
||||
Select(`label_id, ` + labelColumns + `, label_value_count`).
|
||||
From("labels").
|
||||
OrderBy("label_key")
|
||||
|
||||
stmt = stmt.Where("(label_space_id = ? OR label_repo_id = ?)", spaceID, repoID)
|
||||
stmt = stmt.Limit(database.Limit(filter.Size))
|
||||
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
||||
if filter.Query != "" {
|
||||
stmt = stmt.Where(
|
||||
"LOWER(label_key) LIKE ?", strings.ToLower(filter.Query))
|
||||
}
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*label
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list labels")
|
||||
}
|
||||
|
||||
return mapSliceLabel(dst), nil
|
||||
}
|
||||
|
||||
func (s *labelStore) ListInScopes(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
scopeIDs []int64,
|
||||
filter *types.LabelFilter,
|
||||
) ([]*types.Label, error) {
|
||||
stmt := database.Builder.
|
||||
Select(`label_id, ` + labelColumns + `, label_value_count`).
|
||||
From("labels")
|
||||
|
||||
stmt = stmt.Where(squirrel.Or{
|
||||
squirrel.Eq{"label_space_id": scopeIDs},
|
||||
squirrel.Eq{"label_repo_id": repoID},
|
||||
}).
|
||||
OrderBy("label_key").
|
||||
OrderBy("label_scope")
|
||||
|
||||
stmt = stmt.Limit(database.Limit(filter.Size))
|
||||
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
||||
if filter.Query != "" {
|
||||
stmt = stmt.Where(
|
||||
"LOWER(label_key) LIKE ?", strings.ToLower(filter.Query))
|
||||
}
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*label
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list labels in hierarchy")
|
||||
}
|
||||
|
||||
return mapSliceLabel(dst), nil
|
||||
}
|
||||
|
||||
func (s *labelStore) ListInfosInScopes(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
spaceIDs []int64,
|
||||
filter *types.AssignableLabelFilter,
|
||||
) ([]*types.LabelInfo, error) {
|
||||
stmt := database.Builder.
|
||||
Select(`
|
||||
label_id
|
||||
,label_space_id
|
||||
,label_repo_id
|
||||
,label_scope
|
||||
,label_key
|
||||
,label_type
|
||||
,label_color`).
|
||||
From("labels").
|
||||
Where(squirrel.Or{
|
||||
squirrel.Eq{"label_space_id": spaceIDs},
|
||||
squirrel.Eq{"label_repo_id": repoID},
|
||||
}).
|
||||
OrderBy("label_key").
|
||||
OrderBy("label_scope")
|
||||
|
||||
stmt = stmt.Limit(database.Limit(filter.Size))
|
||||
stmt = stmt.Offset(database.Offset(filter.Page, filter.Size))
|
||||
if filter.Query != "" {
|
||||
stmt = stmt.Where(
|
||||
"LOWER(label_key) LIKE ?", strings.ToLower(filter.Query))
|
||||
}
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*labelInfo
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list labels")
|
||||
}
|
||||
|
||||
return mapLabelInfos(dst), nil
|
||||
}
|
||||
|
||||
func mapLabel(lbl *label) *types.Label {
|
||||
return &types.Label{
|
||||
ID: lbl.ID,
|
||||
SpaceID: lbl.SpaceID.Ptr(),
|
||||
RepoID: lbl.RepoID.Ptr(),
|
||||
Scope: lbl.Scope,
|
||||
Key: lbl.Key,
|
||||
Type: lbl.Type,
|
||||
Description: lbl.Description,
|
||||
ValueCount: lbl.ValueCount,
|
||||
Color: lbl.Color,
|
||||
Created: lbl.Created,
|
||||
Updated: lbl.Updated,
|
||||
CreatedBy: lbl.CreatedBy,
|
||||
UpdatedBy: lbl.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func mapSliceLabel(dbLabels []*label) []*types.Label {
|
||||
result := make([]*types.Label, len(dbLabels))
|
||||
|
||||
for i, lbl := range dbLabels {
|
||||
result[i] = mapLabel(lbl)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func mapInternalLabel(lbl *types.Label) *label {
|
||||
return &label{
|
||||
ID: lbl.ID,
|
||||
SpaceID: null.IntFromPtr(lbl.SpaceID),
|
||||
RepoID: null.IntFromPtr(lbl.RepoID),
|
||||
Scope: lbl.Scope,
|
||||
Key: lbl.Key,
|
||||
Description: lbl.Description,
|
||||
Type: lbl.Type,
|
||||
Color: lbl.Color,
|
||||
Created: lbl.Created,
|
||||
Updated: lbl.Updated,
|
||||
CreatedBy: lbl.CreatedBy,
|
||||
UpdatedBy: lbl.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func mapLabelInfo(internal *labelInfo) *types.LabelInfo {
|
||||
return &types.LabelInfo{
|
||||
ID: internal.LabelID,
|
||||
RepoID: internal.RepoID.Ptr(),
|
||||
SpaceID: internal.SpaceID.Ptr(),
|
||||
Scope: internal.Scope,
|
||||
Key: internal.Key,
|
||||
Type: internal.Type,
|
||||
Color: internal.LabelColor,
|
||||
}
|
||||
}
|
||||
|
||||
func mapLabelInfos(
|
||||
dbLabels []*labelInfo,
|
||||
) []*types.LabelInfo {
|
||||
result := make([]*types.LabelInfo, len(dbLabels))
|
||||
|
||||
for i, lbl := range dbLabels {
|
||||
result[i] = mapLabelInfo(lbl)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
196
app/store/database/label_pullreq.go
Normal file
196
app/store/database/label_pullreq.go
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var _ store.PullReqLabelAssignmentStore = (*pullReqLabelStore)(nil)
|
||||
|
||||
func NewPullReqLabelStore(db *sqlx.DB) store.PullReqLabelAssignmentStore {
|
||||
return &pullReqLabelStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type pullReqLabelStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
type pullReqLabel struct {
|
||||
PullReqID int64 `db:"pullreq_label_pullreq_id"`
|
||||
LabelID int64 `db:"pullreq_label_label_id"`
|
||||
LabelValueID null.Int `db:"pullreq_label_label_value_id"`
|
||||
Created int64 `db:"pullreq_label_created"`
|
||||
Updated int64 `db:"pullreq_label_updated"`
|
||||
CreatedBy int64 `db:"pullreq_label_created_by"`
|
||||
UpdatedBy int64 `db:"pullreq_label_updated_by"`
|
||||
}
|
||||
|
||||
const (
|
||||
pullReqLabelColumns = `
|
||||
pullreq_label_pullreq_id
|
||||
,pullreq_label_label_id
|
||||
,pullreq_label_label_value_id
|
||||
,pullreq_label_created
|
||||
,pullreq_label_updated
|
||||
,pullreq_label_created_by
|
||||
,pullreq_label_updated_by`
|
||||
)
|
||||
|
||||
func (s *pullReqLabelStore) Assign(ctx context.Context, label *types.PullReqLabel) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO pullreq_labels (` + pullReqLabelColumns + `)
|
||||
values (
|
||||
:pullreq_label_pullreq_id
|
||||
,:pullreq_label_label_id
|
||||
,:pullreq_label_label_value_id
|
||||
,:pullreq_label_created
|
||||
,:pullreq_label_updated
|
||||
,:pullreq_label_created_by
|
||||
,:pullreq_label_updated_by
|
||||
)
|
||||
ON CONFLICT (pullreq_label_pullreq_id, pullreq_label_label_id)
|
||||
DO UPDATE SET
|
||||
pullreq_label_label_value_id = EXCLUDED.pullreq_label_label_value_id,
|
||||
pullreq_label_updated = EXCLUDED.pullreq_label_updated,
|
||||
pullreq_label_updated_by = EXCLUDED.pullreq_label_updated_by
|
||||
RETURNING pullreq_label_created, pullreq_label_created_by
|
||||
`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalPullReqLabel(label))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "failed to bind query")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, args...).Scan(&label.Created, &label.CreatedBy); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "failed to create pull request label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *pullReqLabelStore) Unassign(ctx context.Context, pullreqID int64, labelID int64) error {
|
||||
const sqlQuery = `
|
||||
DELETE FROM pullreq_labels
|
||||
WHERE pullreq_label_pullreq_id = $1 AND pullreq_label_label_id = $2`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, sqlQuery, pullreqID, labelID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "failed to delete pullreq label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *pullReqLabelStore) FindByLabelID(
|
||||
ctx context.Context,
|
||||
pullreqID int64,
|
||||
labelID int64,
|
||||
) (*types.PullReqLabel, error) {
|
||||
const sqlQuery = `SELECT ` + pullReqLabelColumns + `
|
||||
FROM pullreq_labels
|
||||
WHERE pullreq_label_pullreq_id = $1 AND pullreq_label_label_id = $2`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst pullReqLabel
|
||||
if err := db.GetContext(ctx, &dst, sqlQuery, pullreqID, labelID); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "failed to delete pullreq label")
|
||||
}
|
||||
|
||||
return mapPullReqLabel(&dst), nil
|
||||
}
|
||||
|
||||
func (s *pullReqLabelStore) ListAssigned(
|
||||
ctx context.Context,
|
||||
pullreqID int64,
|
||||
) (map[int64]*types.LabelAssignment, error) {
|
||||
const sqlQueryAssigned = `
|
||||
SELECT
|
||||
label_id
|
||||
,label_repo_id
|
||||
,label_space_id
|
||||
,label_key
|
||||
,label_value_id
|
||||
,label_value_label_id
|
||||
,label_value_value
|
||||
,label_color
|
||||
,label_value_color
|
||||
,label_scope
|
||||
,label_type
|
||||
FROM pullreq_labels prl
|
||||
INNER JOIN labels l ON prl.pullreq_label_label_id = l.label_id
|
||||
LEFT JOIN label_values lv ON prl.pullreq_label_label_value_id = lv.label_value_id
|
||||
WHERE prl.pullreq_label_pullreq_id = $1`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*struct {
|
||||
labelInfo
|
||||
labelValueInfo
|
||||
}
|
||||
if err := db.SelectContext(ctx, &dst, sqlQueryAssigned, pullreqID); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "failed to find label")
|
||||
}
|
||||
|
||||
ret := make(map[int64]*types.LabelAssignment, len(dst))
|
||||
for _, res := range dst {
|
||||
li := mapLabelInfo(&res.labelInfo)
|
||||
lvi := mapLabeValuelInfo(&res.labelValueInfo)
|
||||
ret[li.ID] = &types.LabelAssignment{
|
||||
LabelInfo: *li,
|
||||
AssignedValue: lvi,
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func mapInternalPullReqLabel(lbl *types.PullReqLabel) *pullReqLabel {
|
||||
return &pullReqLabel{
|
||||
PullReqID: lbl.PullReqID,
|
||||
LabelID: lbl.LabelID,
|
||||
LabelValueID: null.IntFromPtr(lbl.ValueID),
|
||||
Created: lbl.Created,
|
||||
Updated: lbl.Updated,
|
||||
CreatedBy: lbl.CreatedBy,
|
||||
UpdatedBy: lbl.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func mapPullReqLabel(lbl *pullReqLabel) *types.PullReqLabel {
|
||||
return &types.PullReqLabel{
|
||||
PullReqID: lbl.PullReqID,
|
||||
LabelID: lbl.LabelID,
|
||||
ValueID: lbl.LabelValueID.Ptr(),
|
||||
Created: lbl.Created,
|
||||
Updated: lbl.Updated,
|
||||
CreatedBy: lbl.CreatedBy,
|
||||
UpdatedBy: lbl.UpdatedBy,
|
||||
}
|
||||
}
|
358
app/store/database/label_value.go
Normal file
358
app/store/database/label_value.go
Normal file
@ -0,0 +1,358 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
labelValueColumns = `
|
||||
label_value_label_id
|
||||
,label_value_value
|
||||
,label_value_color
|
||||
,label_value_created
|
||||
,label_value_updated
|
||||
,label_value_created_by
|
||||
,label_value_updated_by`
|
||||
|
||||
labelValueSelectBase = `SELECT label_value_id, ` + labelValueColumns + ` FROM label_values`
|
||||
)
|
||||
|
||||
type labelValue struct {
|
||||
ID int64 `db:"label_value_id"`
|
||||
LabelID int64 `db:"label_value_label_id"`
|
||||
Value string `db:"label_value_value"`
|
||||
Color enum.LabelColor `db:"label_value_color"`
|
||||
Created int64 `db:"label_value_created"`
|
||||
Updated int64 `db:"label_value_updated"`
|
||||
CreatedBy int64 `db:"label_value_created_by"`
|
||||
UpdatedBy int64 `db:"label_value_updated_by"`
|
||||
}
|
||||
|
||||
type labelValueInfo struct {
|
||||
ValueID null.Int `db:"label_value_id"`
|
||||
LabelID null.Int `db:"label_value_label_id"`
|
||||
Value null.String `db:"label_value_value"`
|
||||
ValueColor null.String `db:"label_value_color"`
|
||||
}
|
||||
|
||||
type labelValueStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func NewLabelValueStore(
|
||||
db *sqlx.DB,
|
||||
) store.LabelValueStore {
|
||||
return &labelValueStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
var _ store.LabelValueStore = (*labelValueStore)(nil)
|
||||
|
||||
func (s *labelValueStore) Define(ctx context.Context, lblVal *types.LabelValue) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO label_values (` + labelValueColumns + `)` + `
|
||||
values (
|
||||
:label_value_label_id
|
||||
,:label_value_value
|
||||
,:label_value_color
|
||||
,:label_value_created
|
||||
,:label_value_updated
|
||||
,:label_value_created_by
|
||||
,:label_value_updated_by
|
||||
)
|
||||
RETURNING label_value_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalLabelValue(lblVal))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, args...).Scan(&lblVal.ID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to create label value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) Update(ctx context.Context, lblVal *types.LabelValue) error {
|
||||
const sqlQuery = `
|
||||
UPDATE label_values SET
|
||||
label_value_value = :label_value_value
|
||||
,label_value_color = :label_value_color
|
||||
,label_value_updated = :label_value_updated
|
||||
,label_value_updated_by = :label_value_updated_by
|
||||
WHERE label_value_id = :label_value_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalLabelValue(lblVal))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
||||
}
|
||||
|
||||
if _, err := db.ExecContext(ctx, query, args...); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to update label value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) Upsert(ctx context.Context, lblVal *types.LabelValue) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO label_values (` + labelValueColumns + `)
|
||||
VALUES (
|
||||
:label_value_label_id,
|
||||
:label_value_value,
|
||||
:label_value_color,
|
||||
:label_value_created,
|
||||
:label_value_updated,
|
||||
:label_value_created_by,
|
||||
:label_value_updated_by
|
||||
)
|
||||
ON CONFLICT (label_value_id) DO UPDATE SET
|
||||
label_value_value = EXCLUDED.label_value_value,
|
||||
label_value_color = EXCLUDED.label_value_color,
|
||||
label_value_updated = EXCLUDED.label_value_updated,
|
||||
label_value_updated_by = EXCLUDED.label_value_updated_by
|
||||
RETURNING label_value_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
query, args, err := db.BindNamed(sqlQuery, mapInternalLabelValue(lblVal))
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, args...).Scan(&lblVal.ID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to upsert label value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) Delete(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
value string,
|
||||
) error {
|
||||
const sqlQuery = `
|
||||
DELETE FROM label_values
|
||||
WHERE label_value_label_id = $1 AND LOWER(label_value_value) = LOWER($2)`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, sqlQuery, labelID, value); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to delete label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) DeleteMany(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
values []string,
|
||||
) error {
|
||||
stmt := database.Builder.
|
||||
Delete("label_values").
|
||||
Where("label_value_label_id = ?", labelID).
|
||||
Where(squirrel.Eq{"label_value_value": values})
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to delete label")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns a list of label values for a specified label.
|
||||
func (s *labelValueStore) List(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
opts *types.ListQueryFilter,
|
||||
) ([]*types.LabelValue, error) {
|
||||
stmt := database.Builder.
|
||||
Select(`label_value_id, ` + labelValueColumns).
|
||||
From("label_values")
|
||||
|
||||
stmt = stmt.Where("label_value_label_id = ?", labelID)
|
||||
|
||||
stmt = stmt.Limit(database.Limit(opts.Size))
|
||||
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size))
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*labelValue
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Fail to list labels")
|
||||
}
|
||||
|
||||
return mapSliceLabelValue(dst), nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) ListInfosByLabelIDs(
|
||||
ctx context.Context,
|
||||
labelIDs []int64,
|
||||
) (map[int64][]*types.LabelValueInfo, error) {
|
||||
stmt := database.Builder.
|
||||
Select(`
|
||||
label_value_id
|
||||
,label_value_label_id
|
||||
,label_value_value
|
||||
,label_value_color
|
||||
`).
|
||||
From("label_values").
|
||||
Where(squirrel.Eq{"label_value_label_id": labelIDs}).
|
||||
OrderBy("label_value_value")
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*labelValueInfo
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Fail to list labels")
|
||||
}
|
||||
|
||||
valueInfos := mapLabelValuInfos(dst)
|
||||
labelValueMap := make(map[int64][]*types.LabelValueInfo)
|
||||
for _, info := range valueInfos {
|
||||
labelValueMap[*info.LabelID] = append(labelValueMap[*info.LabelID], info)
|
||||
}
|
||||
|
||||
return labelValueMap, nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) FindByLabelID(
|
||||
ctx context.Context,
|
||||
labelID int64,
|
||||
value string,
|
||||
) (*types.LabelValue, error) {
|
||||
const sqlQuery = labelValueSelectBase + `
|
||||
WHERE label_value_label_id = $1 AND LOWER(label_value_value) = LOWER($2)`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst labelValue
|
||||
if err := db.GetContext(ctx, &dst, sqlQuery, labelID, value); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
||||
}
|
||||
|
||||
return mapLabelValue(&dst), nil
|
||||
}
|
||||
|
||||
func (s *labelValueStore) FindByID(ctx context.Context, id int64) (*types.LabelValue, error) {
|
||||
const sqlQuery = labelValueSelectBase + `
|
||||
WHERE label_value_id = $1`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst labelValue
|
||||
if err := db.GetContext(ctx, &dst, sqlQuery, id); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
||||
}
|
||||
|
||||
return mapLabelValue(&dst), nil
|
||||
}
|
||||
|
||||
func mapLabelValue(lbl *labelValue) *types.LabelValue {
|
||||
return &types.LabelValue{
|
||||
ID: lbl.ID,
|
||||
LabelID: lbl.LabelID,
|
||||
Value: lbl.Value,
|
||||
Color: lbl.Color,
|
||||
Created: lbl.Created,
|
||||
Updated: lbl.Updated,
|
||||
CreatedBy: lbl.CreatedBy,
|
||||
UpdatedBy: lbl.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func mapSliceLabelValue(dbLabelValues []*labelValue) []*types.LabelValue {
|
||||
result := make([]*types.LabelValue, len(dbLabelValues))
|
||||
|
||||
for i, lbl := range dbLabelValues {
|
||||
result[i] = mapLabelValue(lbl)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func mapInternalLabelValue(lblVal *types.LabelValue) *labelValue {
|
||||
return &labelValue{
|
||||
ID: lblVal.ID,
|
||||
LabelID: lblVal.LabelID,
|
||||
Value: lblVal.Value,
|
||||
Color: lblVal.Color,
|
||||
Created: lblVal.Created,
|
||||
Updated: lblVal.Updated,
|
||||
CreatedBy: lblVal.CreatedBy,
|
||||
UpdatedBy: lblVal.UpdatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func mapLabeValuelInfo(internal *labelValueInfo) *types.LabelValueInfo {
|
||||
if !internal.ValueID.Valid {
|
||||
return nil
|
||||
}
|
||||
return &types.LabelValueInfo{
|
||||
ID: internal.ValueID.Ptr(),
|
||||
LabelID: internal.LabelID.Ptr(),
|
||||
Value: internal.Value.Ptr(),
|
||||
Color: internal.ValueColor.Ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
func mapLabelValuInfos(
|
||||
dbLabels []*labelValueInfo,
|
||||
) []*types.LabelValueInfo {
|
||||
result := make([]*types.LabelValueInfo, len(dbLabels))
|
||||
|
||||
for i, lbl := range dbLabels {
|
||||
result[i] = mapLabeValuelInfo(lbl)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
DROP TABLE pullreq_labels;
|
||||
DROP TABLE label_values;
|
||||
DROP TABLE labels;
|
@ -0,0 +1,78 @@
|
||||
CREATE TABLE labels (
|
||||
label_id SERIAL PRIMARY KEY,
|
||||
label_space_id INTEGER DEFAULT NULL,
|
||||
label_repo_id INTEGER DEFAULT NULL,
|
||||
label_scope INTEGER DEFAULT 0,
|
||||
label_key TEXT NOT NULL,
|
||||
label_description TEXT NOT NULL DEFAULT '',
|
||||
label_color TEXT NOT NULL DEFAULT 'black',
|
||||
label_type TEXT NOT NULL DEFAULT 'static',
|
||||
label_created BIGINT NOT NULL,
|
||||
label_updated BIGINT NOT NULL,
|
||||
label_created_by INTEGER NOT NULL,
|
||||
label_updated_by INTEGER NOT NULL,
|
||||
label_value_count INTEGER DEFAULT 0,
|
||||
|
||||
CONSTRAINT fk_labels_space_id FOREIGN KEY (label_space_id)
|
||||
REFERENCES spaces (space_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_labels_repo_id FOREIGN KEY (label_repo_id)
|
||||
REFERENCES repositories (repo_id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_label_space_or_repo
|
||||
CHECK (label_space_id IS NULL OR label_repo_id IS NULL),
|
||||
CONSTRAINT fk_labels_created_by FOREIGN KEY (label_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_labels_updated_by FOREIGN KEY (label_updated_by)
|
||||
REFERENCES principals (principal_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX labels_space_id_key
|
||||
ON labels(label_space_id, LOWER(label_key))
|
||||
WHERE label_space_id IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX labels_repo_id_key
|
||||
ON labels(label_repo_id, LOWER(label_key))
|
||||
WHERE label_repo_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE label_values (
|
||||
label_value_id SERIAL PRIMARY KEY,
|
||||
label_value_label_id INTEGER NOT NULL,
|
||||
label_value_value TEXT NOT NULL,
|
||||
label_value_color TEXT NOT NULL,
|
||||
label_value_created BIGINT NOT NULL,
|
||||
label_value_updated BIGINT NOT NULL,
|
||||
label_value_created_by INTEGER NOT NULL,
|
||||
label_value_updated_by INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT fk_label_values_label_id FOREIGN KEY (label_value_label_id)
|
||||
REFERENCES labels (label_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_label_values_created_by FOREIGN KEY (label_value_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_labels_values_updated_by FOREIGN KEY (label_value_updated_by)
|
||||
REFERENCES principals (principal_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX unique_label_value_label_id_value
|
||||
ON label_values(label_value_label_id, LOWER(label_value_value));
|
||||
|
||||
CREATE TABLE pullreq_labels (
|
||||
pullreq_label_pullreq_id INTEGER NOT NULL,
|
||||
pullreq_label_label_id INTEGER NOT NULL,
|
||||
pullreq_label_label_value_id INTEGER DEFAULT NULL,
|
||||
pullreq_label_created BIGINT NOT NULL,
|
||||
pullreq_label_updated BIGINT NOT NULL,
|
||||
pullreq_label_created_by INTEGER NOT NULL,
|
||||
pullreq_label_updated_by INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT fk_pullreq_labels_pullreq_id FOREIGN KEY (pullreq_label_pullreq_id)
|
||||
REFERENCES pullreqs (pullreq_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_pullreq_labels_label_id FOREIGN KEY (pullreq_label_label_id)
|
||||
REFERENCES labels (label_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_pullreq_labels_label_value_id FOREIGN KEY (pullreq_label_label_value_id)
|
||||
REFERENCES label_values (label_value_id) ON DELETE SET NULL,
|
||||
CONSTRAINT fk_pullreq_labels_created_by FOREIGN KEY (pullreq_label_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_pullreq_labels_updated_by FOREIGN KEY (pullreq_label_updated_by)
|
||||
REFERENCES principals (principal_id),
|
||||
|
||||
PRIMARY KEY (pullreq_label_pullreq_id, pullreq_label_label_id)
|
||||
);
|
@ -0,0 +1,3 @@
|
||||
DROP TABLE pullreq_labels;
|
||||
DROP TABLE label_values;
|
||||
DROP TABLE labels;
|
@ -0,0 +1,78 @@
|
||||
CREATE TABLE labels (
|
||||
label_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
label_space_id INTEGER DEFAULT NULL,
|
||||
label_repo_id INTEGER DEFAULT NULL,
|
||||
label_scope INTEGER DEFAULT 0,
|
||||
label_key TEXT NOT NULL,
|
||||
label_description TEXT NOT NULL DEFAULT '',
|
||||
label_color TEXT NOT NULL DEFAULT 'black',
|
||||
label_type TEXT NOT NULL DEFAULT 'static',
|
||||
label_created BIGINT NOT NULL,
|
||||
label_updated BIGINT NOT NULL,
|
||||
label_created_by INTEGER NOT NULL,
|
||||
label_updated_by INTEGER NOT NULL,
|
||||
label_value_count INTEGER DEFAULT 0,
|
||||
|
||||
CONSTRAINT fk_labels_space_id FOREIGN KEY (label_space_id)
|
||||
REFERENCES spaces (space_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_labels_repo_id FOREIGN KEY (label_repo_id)
|
||||
REFERENCES repositories (repo_id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_label_space_or_repo
|
||||
CHECK (label_space_id IS NULL OR label_repo_id IS NULL),
|
||||
CONSTRAINT fk_labels_created_by FOREIGN KEY (label_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_labels_updated_by FOREIGN KEY (label_updated_by)
|
||||
REFERENCES principals (principal_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX labels_space_id_key
|
||||
ON labels(label_space_id, LOWER(label_key))
|
||||
WHERE label_space_id IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX labels_repo_id_key
|
||||
ON labels(label_repo_id, LOWER(label_key))
|
||||
WHERE label_repo_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE label_values (
|
||||
label_value_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
label_value_label_id INTEGER NOT NULL,
|
||||
label_value_value TEXT NOT NULL,
|
||||
label_value_color TEXT NOT NULL,
|
||||
label_value_created BIGINT NOT NULL,
|
||||
label_value_updated BIGINT NOT NULL,
|
||||
label_value_created_by INTEGER NOT NULL,
|
||||
label_value_updated_by INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT fk_label_values_label_id FOREIGN KEY (label_value_label_id)
|
||||
REFERENCES labels (label_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_label_values_created_by FOREIGN KEY (label_value_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_labels_values_updated_by FOREIGN KEY (label_value_updated_by)
|
||||
REFERENCES principals (principal_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX unique_label_value_label_id_value
|
||||
ON label_values(label_value_label_id, LOWER(label_value_value));
|
||||
|
||||
CREATE TABLE pullreq_labels (
|
||||
pullreq_label_pullreq_id INTEGER NOT NULL,
|
||||
pullreq_label_label_id INTEGER NOT NULL,
|
||||
pullreq_label_label_value_id INTEGER DEFAULT NULL,
|
||||
pullreq_label_created BIGINT NOT NULL,
|
||||
pullreq_label_updated BIGINT NOT NULL,
|
||||
pullreq_label_created_by INTEGER NOT NULL,
|
||||
pullreq_label_updated_by INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT fk_pullreq_labels_pullreq_id FOREIGN KEY (pullreq_label_pullreq_id)
|
||||
REFERENCES pullreqs (pullreq_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_pullreq_labels_label_id FOREIGN KEY (pullreq_label_label_id)
|
||||
REFERENCES labels (label_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_pullreq_labels_label_value_id FOREIGN KEY (pullreq_label_label_value_id)
|
||||
REFERENCES label_values (label_value_id) ON DELETE SET NULL,
|
||||
CONSTRAINT fk_pullreq_labels_created_by FOREIGN KEY (pullreq_label_created_by)
|
||||
REFERENCES principals (principal_id),
|
||||
CONSTRAINT fk_pullreq_labels_updated_by FOREIGN KEY (pullreq_label_updated_by)
|
||||
REFERENCES principals (principal_id),
|
||||
|
||||
PRIMARY KEY (pullreq_label_pullreq_id, pullreq_label_label_id)
|
||||
);
|
@ -192,9 +192,8 @@ func (s *SpaceStore) findByPathAndDeletedAt(
|
||||
return s.find(ctx, spaceID, &deletedAt)
|
||||
}
|
||||
|
||||
// GetRootSpace returns a space where space_parent_id is NULL.
|
||||
func (s *SpaceStore) GetRootSpace(ctx context.Context, spaceID int64) (*types.Space, error) {
|
||||
query := `WITH RECURSIVE SpaceHierarchy AS (
|
||||
const spaceRecursiveQuery = `
|
||||
WITH RECURSIVE SpaceHierarchy(space_hierarchy_id, space_hierarchy_parent_id) AS (
|
||||
SELECT space_id, space_parent_id
|
||||
FROM spaces
|
||||
WHERE space_id = $1
|
||||
@ -203,11 +202,16 @@ func (s *SpaceStore) GetRootSpace(ctx context.Context, spaceID int64) (*types.Sp
|
||||
|
||||
SELECT s.space_id, s.space_parent_id
|
||||
FROM spaces s
|
||||
JOIN SpaceHierarchy h ON s.space_id = h.space_parent_id
|
||||
JOIN SpaceHierarchy h ON s.space_id = h.space_hierarchy_parent_id
|
||||
)
|
||||
SELECT space_id
|
||||
FROM SpaceHierarchy
|
||||
WHERE space_parent_id IS NULL;`
|
||||
`
|
||||
|
||||
// GetRootSpace returns a space where space_parent_id is NULL.
|
||||
func (s *SpaceStore) GetRootSpace(ctx context.Context, spaceID int64) (*types.Space, error) {
|
||||
query := spaceRecursiveQuery + `
|
||||
SELECT space_hierarchy_id
|
||||
FROM SpaceHierarchy
|
||||
WHERE space_hierarchy_parent_id IS NULL;`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
@ -219,6 +223,39 @@ WHERE space_parent_id IS NULL;`
|
||||
return s.Find(ctx, rootID)
|
||||
}
|
||||
|
||||
// GetAncestorIDs returns a list of all space IDs along the recursive path to the root space.
|
||||
func (s *SpaceStore) GetAncestorIDs(ctx context.Context, spaceID int64) ([]int64, error) {
|
||||
query := spaceRecursiveQuery + `
|
||||
SELECT space_hierarchy_id FROM SpaceHierarchy`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var spaceIDs []int64
|
||||
if err := db.SelectContext(ctx, &spaceIDs, query, spaceID); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "failed to get space hierarchy")
|
||||
}
|
||||
|
||||
return spaceIDs, nil
|
||||
}
|
||||
|
||||
func (s *SpaceStore) GetHierarchy(
|
||||
ctx context.Context,
|
||||
spaceID int64,
|
||||
) ([]*types.Space, error) {
|
||||
query := spaceRecursiveQuery + `
|
||||
SELECT ` + spaceColumns + `
|
||||
FROM spaces INNER JOIN SpaceHierarchy ON space_id = space_hierarchy_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
var dst []*space
|
||||
if err := db.SelectContext(ctx, &dst, query, spaceID); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return s.mapToSpaces(ctx, s.db, dst)
|
||||
}
|
||||
|
||||
// Create a new space.
|
||||
func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
||||
if space == nil {
|
||||
|
@ -65,6 +65,9 @@ var WireSet = wire.NewSet(
|
||||
ProvideGitspaceConfigStore,
|
||||
ProvideGitspaceInstanceStore,
|
||||
ProvideGitspaceEventStore,
|
||||
ProvideLabelStore,
|
||||
ProvideLabelValueStore,
|
||||
ProvidePullReqLabelStore,
|
||||
)
|
||||
|
||||
// migrator is helper function to set up the database by performing automated
|
||||
@ -289,3 +292,18 @@ func ProvidePublicKeyStore(db *sqlx.DB) store.PublicKeyStore {
|
||||
func ProvideGitspaceEventStore(db *sqlx.DB) store.GitspaceEventStore {
|
||||
return NewGitspaceEventStore(db)
|
||||
}
|
||||
|
||||
// ProvideLabelStore provides a label store.
|
||||
func ProvideLabelStore(db *sqlx.DB) store.LabelStore {
|
||||
return NewLabelStore(db)
|
||||
}
|
||||
|
||||
// ProvideLabelValueStore provides a label value store.
|
||||
func ProvideLabelValueStore(db *sqlx.DB) store.LabelValueStore {
|
||||
return NewLabelValueStore(db)
|
||||
}
|
||||
|
||||
// ProvideLabelValueStore provides a label value store.
|
||||
func ProvidePullReqLabelStore(db *sqlx.DB) store.PullReqLabelAssignmentStore {
|
||||
return NewPullReqLabelStore(db)
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
infraproviderSvc "github.com/harness/gitness/app/services/infraprovider"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
svclabel "github.com/harness/gitness/app/services/label"
|
||||
locker "github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/metric"
|
||||
"github.com/harness/gitness/app/services/notification"
|
||||
@ -135,6 +136,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
||||
reposettings.WireSet,
|
||||
pullreq.WireSet,
|
||||
controllerwebhook.WireSet,
|
||||
svclabel.WireSet,
|
||||
serviceaccount.WireSet,
|
||||
user.WireSet,
|
||||
upload.WireSet,
|
||||
|
@ -69,6 +69,7 @@ import (
|
||||
"github.com/harness/gitness/app/services/importer"
|
||||
infraprovider2 "github.com/harness/gitness/app/services/infraprovider"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
"github.com/harness/gitness/app/services/label"
|
||||
"github.com/harness/gitness/app/services/locker"
|
||||
"github.com/harness/gitness/app/services/metric"
|
||||
"github.com/harness/gitness/app/services/notification"
|
||||
@ -216,7 +217,11 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
lockerLocker := locker.ProvideLocker(mutexManager)
|
||||
repoIdentifier := check.ProvideRepoIdentifierCheck()
|
||||
repoCheck := repo.ProvideRepoCheck()
|
||||
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, settingsService, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService)
|
||||
labelStore := database.ProvideLabelStore(db)
|
||||
labelValueStore := database.ProvideLabelValueStore(db)
|
||||
pullReqLabelAssignmentStore := database.ProvidePullReqLabelStore(db)
|
||||
labelService := label.ProvideLabel(transactor, spaceStore, labelStore, labelValueStore, pullReqLabelAssignmentStore)
|
||||
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, settingsService, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, lockerLocker, auditService, mutexManager, repoIdentifier, repoCheck, publicaccessService, labelService)
|
||||
reposettingsController := reposettings.ProvideController(authorizer, repoStore, settingsService, auditService)
|
||||
executionStore := database.ProvideExecutionStore(db)
|
||||
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
||||
@ -247,7 +252,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
gitspaceConfigStore := database.ProvideGitspaceConfigStore(db)
|
||||
gitspaceInstanceStore := database.ProvideGitspaceInstanceStore(db)
|
||||
gitspaceService := gitspace.ProvideGitspace(transactor, gitspaceConfigStore, gitspaceInstanceStore, spaceStore)
|
||||
spaceController := space.ProvideController(config, transactor, provider, streamer, spaceIdentifier, authorizer, spacePathStore, pipelineStore, secretStore, connectorStore, templateStore, spaceStore, repoStore, principalStore, repoController, membershipStore, repository, exporterRepository, resourceLimiter, publicaccessService, auditService, gitspaceService)
|
||||
spaceController := space.ProvideController(config, transactor, provider, streamer, spaceIdentifier, authorizer, spacePathStore, pipelineStore, secretStore, connectorStore, templateStore, spaceStore, repoStore, principalStore, repoController, membershipStore, repository, exporterRepository, resourceLimiter, publicaccessService, auditService, gitspaceService, gitspaceConfigStore, gitspaceInstanceStore, labelService)
|
||||
pipelineController := pipeline.ProvideController(repoStore, triggerStore, authorizer, pipelineStore)
|
||||
secretController := secret.ProvideController(encrypter, secretStore, authorizer, spaceStore)
|
||||
triggerController := trigger.ProvideController(authorizer, triggerStore, pipelineStore, repoStore)
|
||||
@ -280,7 +285,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
return nil, err
|
||||
}
|
||||
pullReq := importer.ProvidePullReqImporter(provider, gitInterface, principalStore, repoStore, pullReqStore, pullReqActivityStore, transactor)
|
||||
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq)
|
||||
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker, pullReq, labelService)
|
||||
webhookConfig := server.ProvideWebhookConfig(config)
|
||||
webhookStore := database.ProvideWebhookStore(db)
|
||||
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
|
||||
|
77
types/enum/label.go
Normal file
77
types/enum/label.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 enum
|
||||
|
||||
type LabelType string
|
||||
|
||||
func (LabelType) Enum() []interface{} { return toInterfaceSlice(LabelTypes) }
|
||||
func (t LabelType) Sanitize() (LabelType, bool) { return Sanitize(t, GetAllLabelTypes) }
|
||||
func GetAllLabelTypes() ([]LabelType, LabelType) { return LabelTypes, LabelTypeStatic }
|
||||
|
||||
const (
|
||||
LabelTypeStatic LabelType = "static"
|
||||
LabelTypeDynamic LabelType = "dynamic"
|
||||
)
|
||||
|
||||
var LabelTypes = sortEnum([]LabelType{
|
||||
LabelTypeStatic,
|
||||
LabelTypeDynamic,
|
||||
})
|
||||
|
||||
type LabelColor string
|
||||
|
||||
func (LabelColor) Enum() []interface{} { return toInterfaceSlice(LabelColors) }
|
||||
func (t LabelColor) Sanitize() (LabelColor, bool) { return Sanitize(t, GetAllLabelColors) }
|
||||
func GetAllLabelColors() ([]LabelColor, LabelColor) { return LabelColors, LabelColorBackground }
|
||||
|
||||
const (
|
||||
LabelColorBackground LabelColor = "background"
|
||||
LabelColorStroke LabelColor = "stroke"
|
||||
LabelColorText LabelColor = "text"
|
||||
LabelColorAccent LabelColor = "accent"
|
||||
LabelColorRed LabelColor = "red"
|
||||
LabelColorGreen LabelColor = "green"
|
||||
LabelColorYellow LabelColor = "yellow"
|
||||
LabelColorBlue LabelColor = "blue"
|
||||
LabelColorPink LabelColor = "pink"
|
||||
LabelColorPurple LabelColor = "purple"
|
||||
LabelColorViolet LabelColor = "violet"
|
||||
LabelColorIndigo LabelColor = "indigo"
|
||||
LabelColorCyan LabelColor = "cyan"
|
||||
LabelColorOrange LabelColor = "orange"
|
||||
LabelColorBrown LabelColor = "brown"
|
||||
LabelColorMint LabelColor = "mint"
|
||||
LabelColorLime LabelColor = "lime"
|
||||
)
|
||||
|
||||
var LabelColors = sortEnum([]LabelColor{
|
||||
LabelColorBackground,
|
||||
LabelColorStroke,
|
||||
LabelColorText,
|
||||
LabelColorAccent,
|
||||
LabelColorRed,
|
||||
LabelColorGreen,
|
||||
LabelColorYellow,
|
||||
LabelColorBlue,
|
||||
LabelColorPink,
|
||||
LabelColorPurple,
|
||||
LabelColorViolet,
|
||||
LabelColorIndigo,
|
||||
LabelColorCyan,
|
||||
LabelColorOrange,
|
||||
LabelColorBrown,
|
||||
LabelColorMint,
|
||||
LabelColorLime,
|
||||
})
|
292
types/label.go
Normal file
292
types/label.go
Normal file
@ -0,0 +1,292 @@
|
||||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/harness/gitness/errors"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLabelLength = 50
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
ID int64 `json:"id"`
|
||||
SpaceID *int64 `json:"space_id,omitempty"`
|
||||
RepoID *int64 `json:"repo_id,omitempty"`
|
||||
Scope int64 `json:"scope"`
|
||||
Key string `json:"key"`
|
||||
Description string `json:"description"`
|
||||
Type enum.LabelType `json:"type"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
ValueCount int64 `json:"value_count"`
|
||||
Created int64 `json:"created"`
|
||||
Updated int64 `json:"updated"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
UpdatedBy int64 `json:"updated_by"`
|
||||
}
|
||||
|
||||
type LabelValue struct {
|
||||
ID int64 `json:"id"`
|
||||
LabelID int64 `json:"label_id"`
|
||||
Value string `json:"value"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
Created int64 `json:"created"`
|
||||
Updated int64 `json:"updated"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
UpdatedBy int64 `json:"updated_by"`
|
||||
}
|
||||
|
||||
type LabelWithValues struct {
|
||||
Label `json:"label"`
|
||||
Values []*LabelValue `json:"values"`
|
||||
}
|
||||
|
||||
// Used to assign label to pullreq.
|
||||
type PullReqLabel struct {
|
||||
PullReqID int64 `json:"pullreq_id"`
|
||||
LabelID int64 `json:"label_id"`
|
||||
ValueID *int64 `json:"value_id,omitempty"`
|
||||
Created int64 `json:"created"`
|
||||
Updated int64 `json:"updated"`
|
||||
CreatedBy int64 `json:"created_by"`
|
||||
UpdatedBy int64 `json:"updated_by"`
|
||||
}
|
||||
|
||||
type LabelInfo struct {
|
||||
SpaceID *int64 `json:"-"`
|
||||
RepoID *int64 `json:"-"`
|
||||
Scope int64 `json:"scope"`
|
||||
ID int64 `json:"id"`
|
||||
Type enum.LabelType `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
Assigned *bool `json:"assigned,omitempty"`
|
||||
}
|
||||
|
||||
type LabelValueInfo struct {
|
||||
LabelID *int64 `json:"-"`
|
||||
ID *int64 `json:"id,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
Color *string `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
type LabelAssignment struct {
|
||||
LabelInfo
|
||||
AssignedValue *LabelValueInfo `json:"assigned_value,omitempty"`
|
||||
Values []*LabelValueInfo `json:"values,omitempty"` // query param ?assignable=true
|
||||
}
|
||||
|
||||
type ScopeData struct {
|
||||
// Scope = 0 is repo, scope >= 1 is a depth level of a space
|
||||
Scope int64 `json:"scope"`
|
||||
Space *Space `json:"space,omitempty"`
|
||||
Repo *Repository `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// Used to fetch label and values from a repo and space hierarchy.
|
||||
type ScopesLabels struct {
|
||||
ScopeData []*ScopeData `json:"scope_data"`
|
||||
LabelData []*LabelAssignment `json:"label_data"`
|
||||
}
|
||||
|
||||
// LabelFilter stores label query parameters.
|
||||
type AssignableLabelFilter struct {
|
||||
ListQueryFilter
|
||||
Assignable bool `json:"assignable,omitempty"`
|
||||
}
|
||||
type LabelFilter struct {
|
||||
ListQueryFilter
|
||||
Inherited bool `json:"inherited,omitempty"`
|
||||
}
|
||||
|
||||
type DefineLabelInput struct {
|
||||
Key string `json:"key"`
|
||||
Type enum.LabelType `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
}
|
||||
|
||||
func (in DefineLabelInput) Validate() error {
|
||||
if err := validateLabelText(in.Key, "key"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateLabelType(in.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := validateLabelColor(in.Color)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateLabelInput struct {
|
||||
Key *string `json:"key,omitempty"`
|
||||
Type *enum.LabelType `json:"type,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Color *enum.LabelColor `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
func (in UpdateLabelInput) Validate() error {
|
||||
if in.Key != nil {
|
||||
if err := validateLabelText(*in.Key, "key"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if in.Type != nil {
|
||||
if err := validateLabelType(*in.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if in.Color != nil {
|
||||
err := validateLabelColor(*in.Color)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DefineValueInput struct {
|
||||
Value string `json:"value"`
|
||||
Color enum.LabelColor `json:"color"`
|
||||
}
|
||||
|
||||
func (in DefineValueInput) Validate() error {
|
||||
if err := validateLabelText(in.Value, "value"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateLabelColor(in.Color); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateValueInput struct {
|
||||
Value *string `json:"value"`
|
||||
Color *enum.LabelColor `json:"color"`
|
||||
}
|
||||
|
||||
func (in UpdateValueInput) Validate() error {
|
||||
if in.Value != nil {
|
||||
if err := validateLabelText(*in.Value, "value"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if in.Color != nil {
|
||||
if err := validateLabelColor(*in.Color); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PullReqCreateInput struct {
|
||||
LabelID int64 `json:"label_id"`
|
||||
ValueID *int64 `json:"value_id"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type PullReqUpdateInput struct {
|
||||
LabelValueID *int64 `json:"label_value_id,omitempty"`
|
||||
}
|
||||
|
||||
func (in PullReqCreateInput) Validate() error {
|
||||
if (in.ValueID != nil && *in.ValueID > 0) && in.Value != "" {
|
||||
return errors.InvalidArgument("cannot accept both value id and value")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SaveLabelInput struct {
|
||||
ID int64 `json:"id"`
|
||||
DefineLabelInput
|
||||
}
|
||||
type SaveLabelValueInput struct {
|
||||
ID int64 `json:"id"`
|
||||
DefineValueInput
|
||||
}
|
||||
|
||||
type SaveInput struct {
|
||||
Label SaveLabelInput `json:"label"`
|
||||
Values []*SaveLabelValueInput `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
func (in *SaveInput) Validate() error {
|
||||
if err := in.Label.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, value := range in.Values {
|
||||
if err := value.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var labelTypes, _ = enum.GetAllLabelTypes()
|
||||
|
||||
func validateLabelText(text string, typ string) error {
|
||||
if len(text) == 0 {
|
||||
return errors.InvalidArgument("%s must be a non-empty string", typ)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(text) > maxLabelLength {
|
||||
return errors.InvalidArgument("%s can have at most %d characters", typ, maxLabelLength)
|
||||
}
|
||||
|
||||
for _, ch := range text {
|
||||
if unicode.IsControl(ch) {
|
||||
return errors.InvalidArgument("%s cannot contain control characters", typ)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLabelType(typ enum.LabelType) error {
|
||||
if _, ok := typ.Sanitize(); !ok {
|
||||
return errors.InvalidArgument("label type must be in %v", labelTypes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var colorTypes, _ = enum.GetAllLabelColors()
|
||||
|
||||
func validateLabelColor(color enum.LabelColor) error {
|
||||
_, ok := color.Sanitize()
|
||||
if !ok {
|
||||
return errors.InvalidArgument("color type must be in %v", colorTypes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user