mirror of
https://github.com/harness/drone.git
synced 2025-05-21 19:39:59 +08:00
Merge remote-tracking branch 'origin' into abhinav/CODE-716
This commit is contained in:
commit
286137e64f
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,7 +6,9 @@ _research
|
|||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
web/node_modules
|
web/node_modules
|
||||||
web/dist/files
|
web/dist
|
||||||
|
web/coverage
|
||||||
|
yarn-error*
|
||||||
release
|
release
|
||||||
.idea
|
.idea
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
[](https://github.com/harness/gitness/actions/workflows/ci-lint.yml)
|
|
||||||
[](#)
|
|
||||||
# Pre-Requisites
|
# Pre-Requisites
|
||||||
|
|
||||||
Install the latest stable version of Node and Go version 1.19 or higher, and then install the below Go programs. Ensure the GOPATH [bin directory](https://go.dev/doc/gopath_code#GOPATH) is added to your PATH.
|
Install the latest stable version of Node and Go version 1.19 or higher, and then install the below Go programs. Ensure the GOPATH [bin directory](https://go.dev/doc/gopath_code#GOPATH) is added to your PATH.
|
||||||
|
27
cache/no_cache.go
vendored
Normal file
27
cache/no_cache.go
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NoCache[K any, V any] struct {
|
||||||
|
getter Getter[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoCache[K any, V any](getter Getter[K, V]) NoCache[K, V] {
|
||||||
|
return NoCache[K, V]{
|
||||||
|
getter: getter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c NoCache[K, V]) Stats() (int64, int64) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c NoCache[K, V]) Get(ctx context.Context, key K) (V, error) {
|
||||||
|
return c.getter.Find(ctx, key)
|
||||||
|
}
|
89
cache/redis_cache.go
vendored
Normal file
89
cache/redis_cache.go
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Redis[K any, V any] struct {
|
||||||
|
client redis.UniversalClient
|
||||||
|
duration time.Duration
|
||||||
|
getter Getter[K, V]
|
||||||
|
keyEncoder func(K) string
|
||||||
|
codec Codec[V]
|
||||||
|
countHit int64
|
||||||
|
countMiss int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encoder[V any] interface {
|
||||||
|
Encode(value V) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Decoder[V any] interface {
|
||||||
|
Decode(encoded string) (V, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Codec[V any] interface {
|
||||||
|
Encoder[V]
|
||||||
|
Decoder[V]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedis[K any, V any](
|
||||||
|
client redis.UniversalClient,
|
||||||
|
getter Getter[K, V],
|
||||||
|
keyEncoder func(K) string,
|
||||||
|
codec Codec[V],
|
||||||
|
duration time.Duration,
|
||||||
|
) *Redis[K, V] {
|
||||||
|
return &Redis[K, V]{
|
||||||
|
client: client,
|
||||||
|
duration: duration,
|
||||||
|
getter: getter,
|
||||||
|
keyEncoder: keyEncoder,
|
||||||
|
codec: codec,
|
||||||
|
countHit: 0,
|
||||||
|
countMiss: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats returns number of cache hits and misses and can be used to monitor the cache efficiency.
|
||||||
|
func (c *Redis[K, V]) Stats() (int64, int64) {
|
||||||
|
return c.countHit, c.countMiss
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get implements the cache.Cache interface.
|
||||||
|
func (c *Redis[K, V]) Get(ctx context.Context, key K) (V, error) {
|
||||||
|
var nothing V
|
||||||
|
|
||||||
|
strKey := c.keyEncoder(key)
|
||||||
|
|
||||||
|
raw, err := c.client.Get(ctx, strKey).Result()
|
||||||
|
if err == nil {
|
||||||
|
c.countHit++
|
||||||
|
return c.codec.Decode(raw)
|
||||||
|
}
|
||||||
|
if err != redis.Nil {
|
||||||
|
return nothing, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.countMiss++
|
||||||
|
|
||||||
|
item, err := c.getter.Find(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return nothing, fmt.Errorf("cache: failed to find one: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.client.Set(ctx, strKey, c.codec.Encode(item), c.duration).Err()
|
||||||
|
if err != nil {
|
||||||
|
return nothing, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
@ -5,15 +5,31 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProvideRedis provides a redis client based on the configuration.
|
// ProvideRedis provides a redis client based on the configuration.
|
||||||
// TODO: add support for sentinal / cluster
|
|
||||||
// TODO: add support for TLS
|
// TODO: add support for TLS
|
||||||
func ProvideRedis(config *types.Config) (redis.UniversalClient, error) {
|
func ProvideRedis(config *types.Config) (redis.UniversalClient, error) {
|
||||||
|
if config.Redis.SentinelMode {
|
||||||
|
addrs := strings.Split(config.Redis.SentinelEndpoint, ",")
|
||||||
|
|
||||||
|
failoverOptions := &redis.FailoverOptions{
|
||||||
|
MasterName: config.Redis.SentinelMaster,
|
||||||
|
SentinelAddrs: addrs,
|
||||||
|
MaxRetries: config.Redis.MaxRetries,
|
||||||
|
MinIdleConns: config.Redis.MinIdleConnections,
|
||||||
|
}
|
||||||
|
if config.Redis.Password != "" {
|
||||||
|
failoverOptions.Password = config.Redis.Password
|
||||||
|
}
|
||||||
|
return redis.NewFailoverClient(failoverOptions), nil
|
||||||
|
}
|
||||||
|
|
||||||
options := &redis.Options{
|
options := &redis.Options{
|
||||||
Addr: config.Redis.Endpoint,
|
Addr: config.Redis.Endpoint,
|
||||||
MaxRetries: config.Redis.MaxRetries,
|
MaxRetries: config.Redis.MaxRetries,
|
||||||
|
@ -11,15 +11,19 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
cliserver "github.com/harness/gitness/cli/server"
|
cliserver "github.com/harness/gitness/cli/server"
|
||||||
|
"github.com/harness/gitness/encrypt"
|
||||||
"github.com/harness/gitness/events"
|
"github.com/harness/gitness/events"
|
||||||
"github.com/harness/gitness/gitrpc"
|
"github.com/harness/gitness/gitrpc"
|
||||||
gitrpcserver "github.com/harness/gitness/gitrpc/server"
|
gitrpcserver "github.com/harness/gitness/gitrpc/server"
|
||||||
gitrpccron "github.com/harness/gitness/gitrpc/server/cron"
|
gitrpccron "github.com/harness/gitness/gitrpc/server/cron"
|
||||||
checkcontroller "github.com/harness/gitness/internal/api/controller/check"
|
checkcontroller "github.com/harness/gitness/internal/api/controller/check"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
"github.com/harness/gitness/internal/api/controller/githook"
|
"github.com/harness/gitness/internal/api/controller/githook"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
"github.com/harness/gitness/internal/api/controller/principal"
|
"github.com/harness/gitness/internal/api/controller/principal"
|
||||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||||
"github.com/harness/gitness/internal/api/controller/repo"
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
"github.com/harness/gitness/internal/api/controller/service"
|
"github.com/harness/gitness/internal/api/controller/service"
|
||||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||||
"github.com/harness/gitness/internal/api/controller/space"
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
@ -79,6 +83,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
|||||||
gitrpc.WireSet,
|
gitrpc.WireSet,
|
||||||
store.WireSet,
|
store.WireSet,
|
||||||
check.WireSet,
|
check.WireSet,
|
||||||
|
encrypt.WireSet,
|
||||||
cliserver.ProvideEventsConfig,
|
cliserver.ProvideEventsConfig,
|
||||||
events.WireSet,
|
events.WireSet,
|
||||||
cliserver.ProvideWebhookConfig,
|
cliserver.ProvideWebhookConfig,
|
||||||
@ -90,6 +95,9 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
|||||||
codecomments.WireSet,
|
codecomments.WireSet,
|
||||||
gitrpccron.WireSet,
|
gitrpccron.WireSet,
|
||||||
checkcontroller.WireSet,
|
checkcontroller.WireSet,
|
||||||
|
execution.WireSet,
|
||||||
|
pipeline.WireSet,
|
||||||
|
secret.WireSet,
|
||||||
)
|
)
|
||||||
return &cliserver.System{}, nil
|
return &cliserver.System{}, nil
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,21 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/harness/gitness/cli/server"
|
"github.com/harness/gitness/cli/server"
|
||||||
|
"github.com/harness/gitness/encrypt"
|
||||||
"github.com/harness/gitness/events"
|
"github.com/harness/gitness/events"
|
||||||
"github.com/harness/gitness/gitrpc"
|
"github.com/harness/gitness/gitrpc"
|
||||||
server3 "github.com/harness/gitness/gitrpc/server"
|
server3 "github.com/harness/gitness/gitrpc/server"
|
||||||
"github.com/harness/gitness/gitrpc/server/cron"
|
"github.com/harness/gitness/gitrpc/server/cron"
|
||||||
check2 "github.com/harness/gitness/internal/api/controller/check"
|
check2 "github.com/harness/gitness/internal/api/controller/check"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
"github.com/harness/gitness/internal/api/controller/githook"
|
"github.com/harness/gitness/internal/api/controller/githook"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
"github.com/harness/gitness/internal/api/controller/principal"
|
"github.com/harness/gitness/internal/api/controller/principal"
|
||||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||||
"github.com/harness/gitness/internal/api/controller/repo"
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
"github.com/harness/gitness/internal/api/controller/service"
|
"github.com/harness/gitness/internal/api/controller/service"
|
||||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||||
"github.com/harness/gitness/internal/api/controller/space"
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
@ -84,7 +89,17 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
repoController := repo.ProvideController(config, db, provider, pathUID, authorizer, pathStore, repoStore, spaceStore, principalStore, gitrpcInterface)
|
repoController := repo.ProvideController(config, db, provider, pathUID, authorizer, pathStore, repoStore, spaceStore, principalStore, gitrpcInterface)
|
||||||
spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, spaceStore, repoStore, principalStore, repoController, membershipStore)
|
executionStore := database.ProvideExecutionStore(db)
|
||||||
|
pipelineStore := database.ProvidePipelineStore(db)
|
||||||
|
executionController := execution.ProvideController(db, authorizer, executionStore, pipelineStore, spaceStore)
|
||||||
|
secretStore := database.ProvideSecretStore(db)
|
||||||
|
spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, pipelineStore, secretStore, spaceStore, repoStore, principalStore, repoController, membershipStore)
|
||||||
|
pipelineController := pipeline.ProvideController(db, pathUID, pathStore, repoStore, authorizer, pipelineStore, spaceStore)
|
||||||
|
encrypter, err := encrypt.ProvideEncrypter(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
secretController := secret.ProvideController(db, pathUID, pathStore, encrypter, secretStore, authorizer, spaceStore)
|
||||||
pullReqStore := database.ProvidePullReqStore(db, principalInfoCache)
|
pullReqStore := database.ProvidePullReqStore(db, principalInfoCache)
|
||||||
pullReqActivityStore := database.ProvidePullReqActivityStore(db, principalInfoCache)
|
pullReqActivityStore := database.ProvidePullReqActivityStore(db, principalInfoCache)
|
||||||
codeCommentView := database.ProvideCodeCommentView(db)
|
codeCommentView := database.ProvideCodeCommentView(db)
|
||||||
@ -138,7 +153,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
principalController := principal.ProvideController(principalStore)
|
principalController := principal.ProvideController(principalStore)
|
||||||
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
||||||
checkController := check2.ProvideController(db, authorizer, repoStore, checkStore, gitrpcInterface)
|
checkController := check2.ProvideController(db, authorizer, repoStore, checkStore, gitrpcInterface)
|
||||||
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, spaceController, pullreqController, webhookController, githookController, serviceaccountController, controller, principalController, checkController)
|
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, executionController, spaceController, pipelineController, secretController, pullreqController, webhookController, githookController, serviceaccountController, controller, principalController, checkController)
|
||||||
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
||||||
webHandler := router.ProvideWebHandler(config)
|
webHandler := router.ProvideWebHandler(config)
|
||||||
routerRouter := router.ProvideRouter(config, apiHandler, gitHandler, webHandler)
|
routerRouter := router.ProvideRouter(config, apiHandler, gitHandler, webHandler)
|
||||||
@ -147,7 +162,9 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gitAdapter, err := server3.ProvideGITAdapter()
|
cacheCache := server3.ProvideGoGitRepoCache()
|
||||||
|
cache2 := server3.ProvideLastCommitCache(serverConfig, universalClient, cacheCache)
|
||||||
|
gitAdapter, err := server3.ProvideGITAdapter(cacheCache, cache2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
84
encrypt/aesgcm.go
Normal file
84
encrypt/aesgcm.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2023 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Aesgcm provides an encrypter that uses the aesgcm encryption
|
||||||
|
// algorithm.
|
||||||
|
type Aesgcm struct {
|
||||||
|
block cipher.Block
|
||||||
|
Compat bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts the plaintext using aesgcm.
|
||||||
|
func (e *Aesgcm) Encrypt(plaintext string) ([]byte, error) {
|
||||||
|
gcm, err := cipher.NewGCM(e.block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
_, err = io.ReadFull(rand.Reader, nonce)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcm.Seal(nonce, nonce, []byte(plaintext), nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the ciphertext using aesgcm.
|
||||||
|
func (e *Aesgcm) Decrypt(ciphertext []byte) (string, error) {
|
||||||
|
gcm, err := cipher.NewGCM(e.block)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < gcm.NonceSize() {
|
||||||
|
// if the decryption utility is running in compatibility
|
||||||
|
// mode, it will return the ciphertext as plain text if
|
||||||
|
// decryption fails. This should be used when running the
|
||||||
|
// database in mixed-mode, where there is a mix of encrypted
|
||||||
|
// and unencrypted content.
|
||||||
|
if e.Compat {
|
||||||
|
return string(ciphertext), nil
|
||||||
|
}
|
||||||
|
return "", errors.New("malformed ciphertext")
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext, err := gcm.Open(nil,
|
||||||
|
ciphertext[:gcm.NonceSize()],
|
||||||
|
ciphertext[gcm.NonceSize():],
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
// if the decryption utility is running in compatibility
|
||||||
|
// mode, it will return the ciphertext as plain text if
|
||||||
|
// decryption fails. This should be used when running the
|
||||||
|
// database in mixed-mode, where there is a mix of encrypted
|
||||||
|
// and unencrypted content.
|
||||||
|
if err != nil && e.Compat {
|
||||||
|
return string(ciphertext), nil
|
||||||
|
}
|
||||||
|
return string(plaintext), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// New provides a new aesgcm encrypter
|
||||||
|
func New(key string, compat bool) (Encrypter, error) {
|
||||||
|
if len(key) != 32 {
|
||||||
|
return nil, errKeySize
|
||||||
|
}
|
||||||
|
b := []byte(key)
|
||||||
|
block, err := aes.NewCipher(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Aesgcm{block: block, Compat: compat}, nil
|
||||||
|
}
|
20
encrypt/encrypt.go
Normal file
20
encrypt/encrypt.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// indicates key size is too small.
|
||||||
|
var errKeySize = errors.New("encryption key must be 32 bytes")
|
||||||
|
|
||||||
|
// Encrypter provides field encryption and decryption.
|
||||||
|
// Encrypted values are currently limited to strings, which is
|
||||||
|
// reflected in the interface design.
|
||||||
|
type Encrypter interface {
|
||||||
|
Encrypt(plaintext string) ([]byte, error)
|
||||||
|
Decrypt(ciphertext []byte) (string, error)
|
||||||
|
}
|
19
encrypt/none.go
Normal file
19
encrypt/none.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2023 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package encrypt
|
||||||
|
|
||||||
|
// none is an encryption strategy that stores secret
|
||||||
|
// values in plain text. This is the default strategy
|
||||||
|
// when no key is specified.
|
||||||
|
type none struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*none) Encrypt(plaintext string) ([]byte, error) {
|
||||||
|
return []byte(plaintext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*none) Decrypt(ciphertext []byte) (string, error) {
|
||||||
|
return string(ciphertext), nil
|
||||||
|
}
|
19
encrypt/wire.go
Normal file
19
encrypt/wire.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WireSet provides a wire set for this package.
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvideEncrypter,
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvideEncrypter(config *types.Config) (Encrypter, error) {
|
||||||
|
if config.Encrypter.Secret == "" {
|
||||||
|
return &none{}, nil
|
||||||
|
}
|
||||||
|
return New(config.Encrypter.Secret, config.Encrypter.MixedContent)
|
||||||
|
}
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/gitrpc/check"
|
||||||
"github.com/harness/gitness/gitrpc/rpc"
|
"github.com/harness/gitness/gitrpc/rpc"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -75,19 +76,24 @@ func (c *Client) CreateBranch(ctx context.Context, params *CreateBranchParams) (
|
|||||||
if params == nil {
|
if params == nil {
|
||||||
return nil, ErrNoParamsProvided
|
return nil, ErrNoParamsProvided
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := check.BranchName(params.BranchName); err != nil {
|
||||||
|
return nil, ErrInvalidArgumentf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := c.refService.CreateBranch(ctx, &rpc.CreateBranchRequest{
|
resp, err := c.refService.CreateBranch(ctx, &rpc.CreateBranchRequest{
|
||||||
Base: mapToRPCWriteRequest(params.WriteParams),
|
Base: mapToRPCWriteRequest(params.WriteParams),
|
||||||
Target: params.Target,
|
Target: params.Target,
|
||||||
BranchName: params.BranchName,
|
BranchName: params.BranchName,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processRPCErrorf(err, "failed to create branch on server")
|
return nil, processRPCErrorf(err, "failed to create '%s' branch on server", params.BranchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
var branch *Branch
|
var branch *Branch
|
||||||
branch, err = mapRPCBranch(resp.GetBranch())
|
branch, err = mapRPCBranch(resp.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to map rpc branch: %w", err)
|
return nil, processRPCErrorf(err, "failed to map rpc branch %v", resp.Branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CreateBranchOutput{
|
return &CreateBranchOutput{
|
||||||
|
86
gitrpc/check/branch.go
Normal file
86
gitrpc/check/branch.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* https://git-scm.com/docs/git-check-ref-format
|
||||||
|
* How to handle various characters in refnames:
|
||||||
|
* 0: An acceptable character for refs
|
||||||
|
* 1: End-of-component
|
||||||
|
* 2: ., look for a preceding . to reject .. in refs
|
||||||
|
* 3: {, look for a preceding @ to reject @{ in refs
|
||||||
|
* 4: A bad character: ASCII control characters, and
|
||||||
|
* ":", "?", "[", "\", "^", "~", SP, or TAB
|
||||||
|
* 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
|
||||||
|
*/
|
||||||
|
var refnameDisposition = [256]byte{
|
||||||
|
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
func BranchName(branch string) error {
|
||||||
|
const lock = ".lock"
|
||||||
|
last := byte('\x00')
|
||||||
|
|
||||||
|
for i := 0; i < len(branch); i++ {
|
||||||
|
ch := branch[i] & 255
|
||||||
|
disp := refnameDisposition[ch]
|
||||||
|
|
||||||
|
switch disp {
|
||||||
|
case 1:
|
||||||
|
if i == 0 {
|
||||||
|
goto out
|
||||||
|
}
|
||||||
|
if last == '/' { // Refname contains "//"
|
||||||
|
return fmt.Errorf("branch '%s' cannot have two consecutive slashes // ", branch)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if last == '.' { // Refname contains ".."
|
||||||
|
return fmt.Errorf("branch '%s' cannot have two consecutive dots .. ", branch)
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if last == '@' { // Refname contains "@{".
|
||||||
|
return fmt.Errorf("branch '%s' cannot contain a sequence @{", branch)
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
return fmt.Errorf("branch '%s' cannot have ASCII control characters "+
|
||||||
|
"(i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere", branch)
|
||||||
|
case 5:
|
||||||
|
return fmt.Errorf("branch '%s' can't be a pattern", branch)
|
||||||
|
}
|
||||||
|
last = ch
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
if last == '\x00' {
|
||||||
|
return errors.New("branch name is empty")
|
||||||
|
}
|
||||||
|
if last == '.' {
|
||||||
|
return fmt.Errorf("branch '%s' cannot have . at the end", branch)
|
||||||
|
}
|
||||||
|
if last == '@' {
|
||||||
|
return fmt.Errorf("branch '%s' cannot be the single character @", branch)
|
||||||
|
}
|
||||||
|
if last == '/' {
|
||||||
|
return fmt.Errorf("branch '%s' cannot have / at the end", branch)
|
||||||
|
}
|
||||||
|
if branch[0] == '.' {
|
||||||
|
return fmt.Errorf("branch '%s' cannot start with '.'", branch)
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(branch, lock) {
|
||||||
|
return fmt.Errorf("branch '%s' cannot end with '%s'", branch, lock)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
215
gitrpc/check/branch_test.go
Normal file
215
gitrpc/check/branch_test.go
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package check
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestBranchName(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
branch string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "happy path",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path, include slash",
|
||||||
|
args: args{
|
||||||
|
branch: "eb/new-branch",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "happy path, test utf-8 chars",
|
||||||
|
args: args{
|
||||||
|
branch: "eb/new\u2318branch",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name empty should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name starts with / should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "/new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains // should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "eb//new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with / should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "eb/new-branch/",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name starts with . should return error",
|
||||||
|
args: args{
|
||||||
|
branch: ".new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains .. should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new..branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with . should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch.",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains ~ should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new~branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains ^ should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "^new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains : should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new:branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains control char should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new\x08branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with .lock should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch.lock",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name starts with ? should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "?new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains ? should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new?branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with ? should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch?",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name starts with [ should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "[new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains [ should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new[branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with [ should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch[",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name starts with * should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "*new-branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name contains * should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new*branch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name ends with * should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-branch*",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name cannot contain a sequence @{ and should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-br@{anch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name cannot be the single character @ and should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "@",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "branch name cannot contain \\ and should return error",
|
||||||
|
args: args{
|
||||||
|
branch: "new-br\\anch",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := BranchName(tt.args.branch); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("validateBranchName() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/harness/gitness/gitrpc/rpc"
|
"github.com/harness/gitness/gitrpc/rpc"
|
||||||
@ -97,6 +98,7 @@ type RenameDetails struct {
|
|||||||
type ListCommitsOutput struct {
|
type ListCommitsOutput struct {
|
||||||
Commits []Commit
|
Commits []Commit
|
||||||
RenameDetails []*RenameDetails
|
RenameDetails []*RenameDetails
|
||||||
|
TotalCommits int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error) {
|
func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*ListCommitsOutput, error) {
|
||||||
@ -122,6 +124,21 @@ func (c *Client) ListCommits(ctx context.Context, params *ListCommitsParams) (*L
|
|||||||
Commits: make([]Commit, 0, 16),
|
Commits: make([]Commit, 0, 16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for list commits header
|
||||||
|
header, err := stream.Header()
|
||||||
|
if err != nil {
|
||||||
|
return nil, processRPCErrorf(err, "failed to read list commits header from stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
values := header.Get("total-commits")
|
||||||
|
if len(values) > 0 && values[0] != "" {
|
||||||
|
total, err := strconv.ParseInt(values[0], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, processRPCErrorf(err, "failed to convert header total-commits")
|
||||||
|
}
|
||||||
|
output.TotalCommits = int(total)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var next *rpc.ListCommitsResponse
|
var next *rpc.ListCommitsResponse
|
||||||
next, err = stream.Recv()
|
next, err = stream.Recv()
|
||||||
|
@ -129,7 +129,7 @@ func ErrInvalidArgumentf(format string, args ...interface{}) *Error {
|
|||||||
func processRPCErrorf(err error, format string, args ...interface{}) error {
|
func processRPCErrorf(err error, format string, args ...interface{}) error {
|
||||||
// create fallback error returned if we can't map it
|
// create fallback error returned if we can't map it
|
||||||
fallbackMsg := fmt.Sprintf(format, args...)
|
fallbackMsg := fmt.Sprintf(format, args...)
|
||||||
fallbackErr := fmt.Errorf("%s: %w", fallbackMsg, err)
|
fallbackErr := NewError(StatusInternal, fallbackMsg)
|
||||||
|
|
||||||
// ensure it's an rpc error
|
// ensure it's an rpc error
|
||||||
st, ok := status.FromError(err)
|
st, ok := status.FromError(err)
|
||||||
|
@ -23,6 +23,7 @@ type Interface interface {
|
|||||||
DeleteBranch(ctx context.Context, params *DeleteBranchParams) error
|
DeleteBranch(ctx context.Context, params *DeleteBranchParams) error
|
||||||
ListBranches(ctx context.Context, params *ListBranchesParams) (*ListBranchesOutput, error)
|
ListBranches(ctx context.Context, params *ListBranchesParams) (*ListBranchesOutput, error)
|
||||||
GetRef(ctx context.Context, params GetRefParams) (GetRefResponse, error)
|
GetRef(ctx context.Context, params GetRefParams) (GetRefResponse, error)
|
||||||
|
PathsDetails(ctx context.Context, params PathsDetailsParams) (PathsDetailsOutput, error)
|
||||||
|
|
||||||
// UpdateRef creates, updates or deletes a git ref. If the OldValue is defined it must match the reference value
|
// UpdateRef creates, updates or deletes a git ref. If the OldValue is defined it must match the reference value
|
||||||
// prior to the call. To remove a ref use the zero ref as the NewValue. To require the creation of a new one and
|
// prior to the call. To remove a ref use the zero ref as the NewValue. To require the creation of a new one and
|
||||||
|
@ -7,14 +7,22 @@ package gitea
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cache"
|
||||||
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
gitea "code.gitea.io/gitea/modules/git"
|
gitea "code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Adapter struct {
|
type Adapter struct {
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue]
|
||||||
|
lastCommitCache cache.Cache[CommitEntryKey, *types.Commit]
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() (Adapter, error) {
|
func New(
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue],
|
||||||
|
lastCommitCache cache.Cache[CommitEntryKey, *types.Commit],
|
||||||
|
) (Adapter, error) {
|
||||||
// TODO: should be subdir of gitRoot? What is it being used for?
|
// TODO: should be subdir of gitRoot? What is it being used for?
|
||||||
setting.Git.HomePath = "home"
|
setting.Git.HomePath = "home"
|
||||||
|
|
||||||
@ -23,5 +31,8 @@ func New() (Adapter, error) {
|
|||||||
return Adapter{}, err
|
return Adapter{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Adapter{}, nil
|
return Adapter{
|
||||||
|
repoCache: repoCache,
|
||||||
|
lastCommitCache: lastCommitCache,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
160
gitrpc/internal/gitea/last_commit_cache.go
Normal file
160
gitrpc/internal/gitea/last_commit_cache.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/gob"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cache"
|
||||||
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
|
gitea "code.gitea.io/gitea/modules/git"
|
||||||
|
gogitplumbing "github.com/go-git/go-git/v5/plumbing"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewInMemoryLastCommitCache(
|
||||||
|
cacheDuration time.Duration,
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue],
|
||||||
|
) cache.Cache[CommitEntryKey, *types.Commit] {
|
||||||
|
return cache.New[CommitEntryKey, *types.Commit](
|
||||||
|
commitEntryGetter{
|
||||||
|
repoCache: repoCache,
|
||||||
|
},
|
||||||
|
cacheDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedisLastCommitCache(
|
||||||
|
redisClient redis.UniversalClient,
|
||||||
|
cacheDuration time.Duration,
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue],
|
||||||
|
) cache.Cache[CommitEntryKey, *types.Commit] {
|
||||||
|
return cache.NewRedis[CommitEntryKey, *types.Commit](
|
||||||
|
redisClient,
|
||||||
|
commitEntryGetter{
|
||||||
|
repoCache: repoCache,
|
||||||
|
},
|
||||||
|
func(key CommitEntryKey) string {
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(key))
|
||||||
|
return "gitrpc:last_commit:" + hex.EncodeToString(h.Sum(nil))
|
||||||
|
},
|
||||||
|
commitValueCodec{},
|
||||||
|
cacheDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoLastCommitCache(
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue],
|
||||||
|
) cache.Cache[CommitEntryKey, *types.Commit] {
|
||||||
|
return cache.NewNoCache[CommitEntryKey, *types.Commit](commitEntryGetter{repoCache: repoCache})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommitEntryKey string
|
||||||
|
|
||||||
|
const commitEntryKeySeparator = "\x00"
|
||||||
|
|
||||||
|
func makeCommitEntryKey(repoPath, commitSHA, path string) CommitEntryKey {
|
||||||
|
return CommitEntryKey(repoPath + commitEntryKeySeparator + commitSHA + commitEntryKeySeparator + path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommitEntryKey) Split() (repoPath, commitSHA, path string) {
|
||||||
|
parts := strings.Split(string(c), commitEntryKeySeparator)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPath = parts[0]
|
||||||
|
commitSHA = parts[1]
|
||||||
|
path = parts[2]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type commitValueCodec struct{}
|
||||||
|
|
||||||
|
func (c commitValueCodec) Encode(v *types.Commit) string {
|
||||||
|
buffer := &strings.Builder{}
|
||||||
|
_ = gob.NewEncoder(buffer).Encode(v)
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c commitValueCodec) Decode(s string) (*types.Commit, error) {
|
||||||
|
commit := &types.Commit{}
|
||||||
|
if err := gob.NewDecoder(strings.NewReader(s)).Decode(commit); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unpack commit entry value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return commit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type commitEntryGetter struct {
|
||||||
|
repoCache cache.Cache[string, *RepoEntryValue]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find implements the cache.Getter interface.
|
||||||
|
func (c commitEntryGetter) Find(ctx context.Context, key CommitEntryKey) (*types.Commit, error) {
|
||||||
|
repoPath, rev, path := key.Split()
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
path = "."
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"log", "--max-count=1", "--format=%H", rev, "--", path}
|
||||||
|
commitSHA, _, runErr := gitea.NewCommand(ctx, args...).RunStdString(&gitea.RunOpts{Dir: repoPath})
|
||||||
|
if runErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to run git: %w", runErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
commitSHA = strings.TrimSpace(commitSHA)
|
||||||
|
|
||||||
|
if commitSHA == "" {
|
||||||
|
return nil, types.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := c.repoCache.Get(ctx, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get repository %s from cache: %w", repoPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
commit, err := repo.Repo().CommitObject(gogitplumbing.NewHash(commitSHA))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load commit data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
var message string
|
||||||
|
|
||||||
|
title = commit.Message
|
||||||
|
if idx := strings.IndexRune(commit.Message, '\n'); idx >= 0 {
|
||||||
|
title = commit.Message[:idx]
|
||||||
|
message = commit.Message[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.Commit{
|
||||||
|
SHA: commitSHA,
|
||||||
|
Title: title,
|
||||||
|
Message: message,
|
||||||
|
Author: types.Signature{
|
||||||
|
Identity: types.Identity{
|
||||||
|
Name: commit.Author.Name,
|
||||||
|
Email: commit.Author.Email,
|
||||||
|
},
|
||||||
|
When: commit.Author.When,
|
||||||
|
},
|
||||||
|
Committer: types.Signature{
|
||||||
|
Identity: types.Identity{
|
||||||
|
Name: commit.Committer.Name,
|
||||||
|
Email: commit.Committer.Email,
|
||||||
|
},
|
||||||
|
When: commit.Committer.When,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/harness/gitness/gitrpc/internal/types"
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
gitea "code.gitea.io/gitea/modules/git"
|
gitea "code.gitea.io/gitea/modules/git"
|
||||||
|
gogitfilemode "github.com/go-git/go-git/v5/plumbing/filemode"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -132,21 +133,21 @@ func mapGiteaCommit(giteaCommit *gitea.Commit) (*types.Commit, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapGiteaNodeToTreeNodeModeAndType(giteaMode gitea.EntryMode) (types.TreeNodeType, types.TreeNodeMode, error) {
|
func mapGogitNodeToTreeNodeModeAndType(gogitMode gogitfilemode.FileMode) (types.TreeNodeType, types.TreeNodeMode, error) {
|
||||||
switch giteaMode {
|
switch gogitMode {
|
||||||
case gitea.EntryModeBlob:
|
case gogitfilemode.Regular, gogitfilemode.Deprecated:
|
||||||
return types.TreeNodeTypeBlob, types.TreeNodeModeFile, nil
|
return types.TreeNodeTypeBlob, types.TreeNodeModeFile, nil
|
||||||
case gitea.EntryModeSymlink:
|
case gogitfilemode.Symlink:
|
||||||
return types.TreeNodeTypeBlob, types.TreeNodeModeSymlink, nil
|
return types.TreeNodeTypeBlob, types.TreeNodeModeSymlink, nil
|
||||||
case gitea.EntryModeExec:
|
case gogitfilemode.Executable:
|
||||||
return types.TreeNodeTypeBlob, types.TreeNodeModeExec, nil
|
return types.TreeNodeTypeBlob, types.TreeNodeModeExec, nil
|
||||||
case gitea.EntryModeCommit:
|
case gogitfilemode.Submodule:
|
||||||
return types.TreeNodeTypeCommit, types.TreeNodeModeCommit, nil
|
return types.TreeNodeTypeCommit, types.TreeNodeModeCommit, nil
|
||||||
case gitea.EntryModeTree:
|
case gogitfilemode.Dir:
|
||||||
return types.TreeNodeTypeTree, types.TreeNodeModeTree, nil
|
return types.TreeNodeTypeTree, types.TreeNodeModeTree, nil
|
||||||
default:
|
default:
|
||||||
return types.TreeNodeTypeBlob, types.TreeNodeModeFile,
|
return types.TreeNodeTypeBlob, types.TreeNodeModeFile,
|
||||||
fmt.Errorf("received unknown tree node mode from gitea: '%s'", giteaMode.String())
|
fmt.Errorf("received unknown tree node mode from gogit: '%s'", gogitMode.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
82
gitrpc/internal/gitea/paths_details.go
Normal file
82
gitrpc/internal/gitea/paths_details.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
|
gogitplumbing "github.com/go-git/go-git/v5/plumbing"
|
||||||
|
gogitfilemode "github.com/go-git/go-git/v5/plumbing/filemode"
|
||||||
|
gogitobject "github.com/go-git/go-git/v5/plumbing/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PathsDetails returns additional details about provided the paths.
|
||||||
|
func (g Adapter) PathsDetails(ctx context.Context,
|
||||||
|
repoPath string,
|
||||||
|
rev string,
|
||||||
|
paths []string,
|
||||||
|
) ([]types.PathDetails, error) {
|
||||||
|
repoEntry, err := g.repoCache.Get(ctx, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open repository: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := repoEntry.Repo()
|
||||||
|
|
||||||
|
refSHA, err := repo.ResolveRevision(gogitplumbing.Revision(rev))
|
||||||
|
if errors.Is(err, gogitplumbing.ErrReferenceNotFound) {
|
||||||
|
return nil, types.ErrNotFound
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve revision %s: %w", rev, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
refCommit, err := repo.CommitObject(*refSHA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load commit data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := refCommit.Tree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get tree for the commit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]types.PathDetails, len(paths))
|
||||||
|
|
||||||
|
for i, path := range paths {
|
||||||
|
results[i].Path = path
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
entry, err := tree.FindEntry(path)
|
||||||
|
if errors.Is(err, gogitobject.ErrDirectoryNotFound) || errors.Is(err, gogitobject.ErrEntryNotFound) {
|
||||||
|
return nil, types.ErrPathNotFound
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't find path entry %s: %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Mode == gogitfilemode.Regular || entry.Mode == gogitfilemode.Executable {
|
||||||
|
blobObj, err := repo.Object(gogitplumbing.BlobObject, entry.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get blob object size for the path %s and hash %s: %w",
|
||||||
|
path, entry.Hash.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
results[i].Size = blobObj.(*gogitobject.Blob).Size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commitEntry, err := g.lastCommitCache.Get(ctx, makeCommitEntryKey(repoPath, refSHA.String(), path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find last commit for path %s: %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
results[i].LastCommit = commitEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
35
gitrpc/internal/gitea/repo_cache.go
Normal file
35
gitrpc/internal/gitea/repo_cache.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package gitea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cache"
|
||||||
|
|
||||||
|
gogit "github.com/go-git/go-git/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRepoCache() cache.Cache[string, *RepoEntryValue] {
|
||||||
|
return cache.New[string, *RepoEntryValue](repoGetter{}, 4*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
type repoGetter struct{}
|
||||||
|
|
||||||
|
type RepoEntryValue gogit.Repository
|
||||||
|
|
||||||
|
func (repo *RepoEntryValue) Repo() *gogit.Repository {
|
||||||
|
return (*gogit.Repository)(repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repoGetter) Find(_ context.Context, path string) (*RepoEntryValue, error) {
|
||||||
|
repo, err := gogit.PlainOpen(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*RepoEntryValue)(repo), nil
|
||||||
|
}
|
@ -7,6 +7,7 @@ package gitea
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
@ -16,6 +17,9 @@ import (
|
|||||||
"github.com/harness/gitness/gitrpc/internal/types"
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
gitea "code.gitea.io/gitea/modules/git"
|
gitea "code.gitea.io/gitea/modules/git"
|
||||||
|
gogitplumbing "github.com/go-git/go-git/v5/plumbing"
|
||||||
|
gogitfilemode "github.com/go-git/go-git/v5/plumbing/filemode"
|
||||||
|
gogitobject "github.com/go-git/go-git/v5/plumbing/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cleanTreePath(treePath string) string {
|
func cleanTreePath(treePath string) string {
|
||||||
@ -24,30 +28,53 @@ func cleanTreePath(treePath string) string {
|
|||||||
|
|
||||||
// GetTreeNode returns the tree node at the given path as found for the provided reference.
|
// GetTreeNode returns the tree node at the given path as found for the provided reference.
|
||||||
// Note: ref can be Branch / Tag / CommitSHA.
|
// Note: ref can be Branch / Tag / CommitSHA.
|
||||||
func (g Adapter) GetTreeNode(ctx context.Context, repoPath string,
|
func (g Adapter) GetTreeNode(ctx context.Context,
|
||||||
ref string, treePath string) (*types.TreeNode, error) {
|
repoPath string,
|
||||||
|
ref string,
|
||||||
|
treePath string,
|
||||||
|
) (*types.TreeNode, error) {
|
||||||
treePath = cleanTreePath(treePath)
|
treePath = cleanTreePath(treePath)
|
||||||
|
|
||||||
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
|
repoEntry, err := g.repoCache.Get(ctx, repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGiteaErrorf(err, "failed to open repository")
|
return nil, processGiteaErrorf(err, "failed to open repository")
|
||||||
}
|
}
|
||||||
defer giteaRepo.Close()
|
|
||||||
|
|
||||||
// Get the giteaCommit object for the ref
|
repo := repoEntry.Repo()
|
||||||
giteaCommit, err := giteaRepo.GetCommit(ref)
|
|
||||||
if err != nil {
|
refSHA, err := repo.ResolveRevision(gogitplumbing.Revision(ref))
|
||||||
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
|
if errors.Is(err, gogitplumbing.ErrReferenceNotFound) {
|
||||||
|
return nil, types.ErrNotFound
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve revision %s: %w", ref, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle ErrNotExist :)
|
refCommit, err := repo.CommitObject(*refSHA)
|
||||||
giteaTreeEntry, err := giteaCommit.GetTreeEntryByPath(treePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGiteaErrorf(err, "failed to get tree entry for commit '%s' at path '%s'",
|
return nil, fmt.Errorf("failed to load commit data: %w", err)
|
||||||
giteaCommit.ID.String(), treePath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeType, mode, err := mapGiteaNodeToTreeNodeModeAndType(giteaTreeEntry.Mode())
|
rootEntry := gogitobject.TreeEntry{
|
||||||
|
Name: "",
|
||||||
|
Mode: gogitfilemode.Dir,
|
||||||
|
Hash: refCommit.TreeHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
treeEntry := &rootEntry
|
||||||
|
|
||||||
|
if len(treePath) > 0 {
|
||||||
|
tree, err := refCommit.Tree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get tree for the commit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
treeEntry, err = tree.FindEntry(treePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't find path entry %s: %w", treePath, types.ErrPathNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeType, mode, err := mapGogitNodeToTreeNodeModeAndType(treeEntry.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -55,106 +82,74 @@ func (g Adapter) GetTreeNode(ctx context.Context, repoPath string,
|
|||||||
return &types.TreeNode{
|
return &types.TreeNode{
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
NodeType: nodeType,
|
NodeType: nodeType,
|
||||||
Sha: giteaTreeEntry.ID.String(),
|
Sha: treeEntry.Hash.String(),
|
||||||
Name: giteaTreeEntry.Name(),
|
Name: treeEntry.Name,
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTreeNodes lists the child nodes of a tree reachable from ref via the specified path
|
// ListTreeNodes lists the child nodes of a tree reachable from ref via the specified path
|
||||||
// and includes the latest commit for all nodes if requested.
|
// and includes the latest commit for all nodes if requested.
|
||||||
// IMPORTANT: recursive and includeLatestCommit can't be used together.
|
|
||||||
// Note: ref can be Branch / Tag / CommitSHA.
|
// Note: ref can be Branch / Tag / CommitSHA.
|
||||||
//
|
//
|
||||||
//nolint:gocognit // refactor if needed
|
//nolint:gocognit // refactor if needed
|
||||||
func (g Adapter) ListTreeNodes(ctx context.Context, repoPath string,
|
func (g Adapter) ListTreeNodes(ctx context.Context,
|
||||||
ref string, treePath string, recursive bool, includeLatestCommit bool) ([]types.TreeNodeWithCommit, error) {
|
repoPath string,
|
||||||
if recursive && includeLatestCommit {
|
ref string,
|
||||||
// To avoid potential performance catastrophe, block recursive with includeLatestCommit
|
treePath string,
|
||||||
// TODO: this should return bad error to caller if needed?
|
) ([]types.TreeNode, error) {
|
||||||
// TODO: should this be refactored in two methods?
|
|
||||||
return nil, fmt.Errorf("latest commit with recursive query is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
treePath = cleanTreePath(treePath)
|
treePath = cleanTreePath(treePath)
|
||||||
|
|
||||||
giteaRepo, err := gitea.OpenRepository(ctx, repoPath)
|
repoEntry, err := g.repoCache.Get(ctx, repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGiteaErrorf(err, "failed to open repository")
|
return nil, processGiteaErrorf(err, "failed to open repository")
|
||||||
}
|
}
|
||||||
defer giteaRepo.Close()
|
|
||||||
|
|
||||||
// Get the giteaCommit object for the ref
|
repo := repoEntry.Repo()
|
||||||
giteaCommit, err := giteaRepo.GetCommit(ref)
|
|
||||||
|
refSHA, err := repo.ResolveRevision(gogitplumbing.Revision(ref))
|
||||||
|
if errors.Is(err, gogitplumbing.ErrReferenceNotFound) {
|
||||||
|
return nil, types.ErrNotFound
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve revision %s: %w", ref, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
refCommit, err := repo.CommitObject(*refSHA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGiteaErrorf(err, "error getting commit for ref '%s'", ref)
|
return nil, fmt.Errorf("failed to load commit data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the giteaTree object for the ref
|
tree, err := refCommit.Tree()
|
||||||
giteaTree, err := giteaCommit.SubTree(treePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGiteaErrorf(err, "error getting tree for '%s'", treePath)
|
return nil, fmt.Errorf("failed to get tree for the commit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var giteaEntries gitea.Entries
|
if len(treePath) > 0 {
|
||||||
if recursive {
|
tree, err = tree.Tree(treePath)
|
||||||
giteaEntries, err = giteaTree.ListEntriesRecursive()
|
if errors.Is(err, gogitobject.ErrDirectoryNotFound) || errors.Is(err, gogitobject.ErrEntryNotFound) {
|
||||||
} else {
|
return nil, types.ErrPathNotFound
|
||||||
giteaEntries, err = giteaTree.ListEntries()
|
} else if err != nil {
|
||||||
}
|
return nil, fmt.Errorf("can't find path entry %s: %w", treePath, err)
|
||||||
if err != nil {
|
|
||||||
return nil, processGiteaErrorf(err, "failed to list entries for tree '%s'", treePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
var latestCommits []gitea.CommitInfo
|
|
||||||
if includeLatestCommit {
|
|
||||||
// TODO: can be speed up with latestCommitCache (currently nil)
|
|
||||||
latestCommits, _, err = giteaEntries.GetCommitsInfo(ctx, giteaCommit, treePath, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, processGiteaErrorf(err, "failed to get latest commits for entries")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(latestCommits) != len(giteaEntries) {
|
|
||||||
return nil, fmt.Errorf("latest commit info doesn't match tree node info - count differs")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]types.TreeNodeWithCommit, len(giteaEntries))
|
treeNodes := make([]types.TreeNode, len(tree.Entries))
|
||||||
for i := range giteaEntries {
|
for i, treeEntry := range tree.Entries {
|
||||||
giteaEntry := giteaEntries[i]
|
nodeType, mode, err := mapGogitNodeToTreeNodeModeAndType(treeEntry.Mode)
|
||||||
|
|
||||||
var nodeType types.TreeNodeType
|
|
||||||
var mode types.TreeNodeMode
|
|
||||||
nodeType, mode, err = mapGiteaNodeToTreeNodeModeAndType(giteaEntry.Mode())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// giteaNode.Name() returns the path of the node relative to the tree.
|
treeNodes[i] = types.TreeNode{
|
||||||
relPath := giteaEntry.Name()
|
NodeType: nodeType,
|
||||||
name := filepath.Base(relPath)
|
Mode: mode,
|
||||||
|
Sha: treeEntry.Hash.String(),
|
||||||
var commit *types.Commit
|
Name: treeEntry.Name,
|
||||||
if includeLatestCommit {
|
Path: filepath.Join(treePath, treeEntry.Name),
|
||||||
commit, err = mapGiteaCommit(latestCommits[i].Commit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[i] = types.TreeNodeWithCommit{
|
|
||||||
TreeNode: types.TreeNode{
|
|
||||||
NodeType: nodeType,
|
|
||||||
Mode: mode,
|
|
||||||
Sha: giteaEntry.ID.String(),
|
|
||||||
Name: name,
|
|
||||||
Path: filepath.Join(treePath, relPath),
|
|
||||||
},
|
|
||||||
Commit: commit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes, nil
|
return treeNodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Adapter) ReadTree(ctx context.Context, repoPath, ref string, w io.Writer, args ...string) error {
|
func (g Adapter) ReadTree(ctx context.Context, repoPath, ref string, w io.Writer, args ...string) error {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/gitrpc/check"
|
||||||
"github.com/harness/gitness/gitrpc/internal/gitea"
|
"github.com/harness/gitness/gitrpc/internal/gitea"
|
||||||
"github.com/harness/gitness/gitrpc/internal/types"
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
"github.com/harness/gitness/gitrpc/rpc"
|
"github.com/harness/gitness/gitrpc/rpc"
|
||||||
@ -21,8 +22,14 @@ import (
|
|||||||
|
|
||||||
var listBranchesRefFields = []types.GitReferenceField{types.GitReferenceFieldRefName, types.GitReferenceFieldObjectName}
|
var listBranchesRefFields = []types.GitReferenceField{types.GitReferenceFieldRefName, types.GitReferenceFieldObjectName}
|
||||||
|
|
||||||
func (s ReferenceService) CreateBranch(ctx context.Context,
|
func (s ReferenceService) CreateBranch(
|
||||||
request *rpc.CreateBranchRequest) (*rpc.CreateBranchResponse, error) {
|
ctx context.Context,
|
||||||
|
request *rpc.CreateBranchRequest,
|
||||||
|
) (*rpc.CreateBranchResponse, error) {
|
||||||
|
if err := check.BranchName(request.BranchName); err != nil {
|
||||||
|
return nil, ErrInvalidArgument(err)
|
||||||
|
}
|
||||||
|
|
||||||
base := request.GetBase()
|
base := request.GetBase()
|
||||||
if base == nil {
|
if base == nil {
|
||||||
return nil, types.ErrBaseCannotBeEmpty
|
return nil, types.ErrBaseCannotBeEmpty
|
||||||
@ -58,7 +65,7 @@ func (s ReferenceService) CreateBranch(ctx context.Context,
|
|||||||
return nil, ErrAlreadyExistsf("branch '%s' already exists", request.GetBranchName())
|
return nil, ErrAlreadyExistsf("branch '%s' already exists", request.GetBranchName())
|
||||||
}
|
}
|
||||||
if !git.IsErrNotExist(err) {
|
if !git.IsErrNotExist(err) {
|
||||||
return nil, fmt.Errorf("branch creation of '%s' failed: %w", request.GetBranchName(), err)
|
return nil, processGitErrorf(err, "branch creation of '%s' failed", request.GetBranchName())
|
||||||
}
|
}
|
||||||
|
|
||||||
// get target commit (as target could be branch/tag/commit, and tag can't be pushed using source:destination syntax)
|
// get target commit (as target could be branch/tag/commit, and tag can't be pushed using source:destination syntax)
|
||||||
@ -67,7 +74,7 @@ func (s ReferenceService) CreateBranch(ctx context.Context,
|
|||||||
return nil, ErrNotFoundf("target '%s' doesn't exist", request.GetTarget())
|
return nil, ErrNotFoundf("target '%s' doesn't exist", request.GetTarget())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get commit id for target '%s': %w", request.GetTarget(), err)
|
return nil, processGitErrorf(err, "failed to get commit id for target '%s'", request.GetTarget())
|
||||||
}
|
}
|
||||||
|
|
||||||
// push to new branch (all changes should go through push flow for hooks and other safety meassures)
|
// push to new branch (all changes should go through push flow for hooks and other safety meassures)
|
||||||
|
@ -6,12 +6,14 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/harness/gitness/gitrpc/internal/types"
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
"github.com/harness/gitness/gitrpc/rpc"
|
"github.com/harness/gitness/gitrpc/rpc"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +67,27 @@ func (s RepositoryService) ListCommits(request *rpc.ListCommitsRequest,
|
|||||||
return processGitErrorf(err, "failed to get list of commits")
|
return processGitErrorf(err, "failed to get list of commits")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try to get total commits between gitref and After refs
|
||||||
|
totalCommits := 0
|
||||||
|
if request.Page == 1 && len(gitCommits) < int(request.Limit) {
|
||||||
|
totalCommits = len(gitCommits)
|
||||||
|
} else if request.After != "" && request.GitRef != request.After {
|
||||||
|
div, err := s.adapter.GetCommitDivergences(ctx, repoPath, []types.CommitDivergenceRequest{
|
||||||
|
{From: request.GitRef, To: request.After},
|
||||||
|
}, 0)
|
||||||
|
if err != nil {
|
||||||
|
return processGitErrorf(err, "failed to get total commits")
|
||||||
|
}
|
||||||
|
if len(div) > 0 {
|
||||||
|
totalCommits = int(div[0].Ahead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Ctx(ctx).Trace().Msgf("git adapter returned %d commits", len(gitCommits))
|
log.Ctx(ctx).Trace().Msgf("git adapter returned %d commits", len(gitCommits))
|
||||||
|
header := metadata.New(map[string]string{"total-commits": strconv.Itoa(totalCommits)})
|
||||||
|
if err := stream.SendHeader(header); err != nil {
|
||||||
|
return ErrInternalf("unable to send 'total-commits' header", err)
|
||||||
|
}
|
||||||
|
|
||||||
for i := range gitCommits {
|
for i := range gitCommits {
|
||||||
var commit *rpc.Commit
|
var commit *rpc.Commit
|
||||||
@ -86,16 +108,6 @@ func (s RepositoryService) ListCommits(request *rpc.ListCommitsRequest,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s RepositoryService) getLatestCommit(ctx context.Context, repoPath string,
|
|
||||||
ref string, path string) (*rpc.Commit, error) {
|
|
||||||
gitCommit, err := s.adapter.GetLatestCommit(ctx, repoPath, ref, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, processGitErrorf(err, "failed to get latest commit")
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapGitCommit(gitCommit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s RepositoryService) GetCommitDivergences(ctx context.Context,
|
func (s RepositoryService) GetCommitDivergences(ctx context.Context,
|
||||||
request *rpc.GetCommitDivergencesRequest) (*rpc.GetCommitDivergencesResponse, error) {
|
request *rpc.GetCommitDivergencesRequest) (*rpc.GetCommitDivergencesResponse, error) {
|
||||||
base := request.GetBase()
|
base := request.GetBase()
|
||||||
|
@ -199,7 +199,8 @@ func processGitErrorf(err error, format string, args ...interface{}) error {
|
|||||||
switch {
|
switch {
|
||||||
case errors.Is(err, types.ErrNotFound),
|
case errors.Is(err, types.ErrNotFound),
|
||||||
errors.Is(err, types.ErrSHADoesNotMatch),
|
errors.Is(err, types.ErrSHADoesNotMatch),
|
||||||
errors.Is(err, types.ErrHunkNotFound):
|
errors.Is(err, types.ErrHunkNotFound),
|
||||||
|
errors.Is(err, types.ErrPathNotFound):
|
||||||
return ErrNotFoundf(format, args...)
|
return ErrNotFoundf(format, args...)
|
||||||
case errors.Is(err, types.ErrAlreadyExists):
|
case errors.Is(err, types.ErrAlreadyExists):
|
||||||
return ErrAlreadyExistsf(format, args...)
|
return ErrAlreadyExistsf(format, args...)
|
||||||
@ -217,6 +218,6 @@ func processGitErrorf(err error, format string, args ...interface{}) error {
|
|||||||
case errors.Is(err, types.ErrFailedToConnect):
|
case errors.Is(err, types.ErrFailedToConnect):
|
||||||
return ErrInvalidArgumentf(format, args...)
|
return ErrInvalidArgumentf(format, args...)
|
||||||
default:
|
default:
|
||||||
return Errorf(codes.Unknown, format, args...)
|
return ErrInternalf(format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ type GitAdapter interface {
|
|||||||
Push(ctx context.Context, repoPath string, opts types.PushOptions) error
|
Push(ctx context.Context, repoPath string, opts types.PushOptions) error
|
||||||
ReadTree(ctx context.Context, repoPath, ref string, w io.Writer, args ...string) error
|
ReadTree(ctx context.Context, repoPath, ref string, w io.Writer, args ...string) error
|
||||||
GetTreeNode(ctx context.Context, repoPath string, ref string, treePath string) (*types.TreeNode, error)
|
GetTreeNode(ctx context.Context, repoPath string, ref string, treePath string) (*types.TreeNode, error)
|
||||||
ListTreeNodes(ctx context.Context, repoPath string, ref string, treePath string,
|
ListTreeNodes(ctx context.Context, repoPath string, ref string, treePath string) ([]types.TreeNode, error)
|
||||||
recursive bool, includeLatestCommit bool) ([]types.TreeNodeWithCommit, error)
|
PathsDetails(ctx context.Context, repoPath string, ref string, paths []string) ([]types.PathDetails, error)
|
||||||
GetSubmodule(ctx context.Context, repoPath string, ref string, treePath string) (*types.Submodule, error)
|
GetSubmodule(ctx context.Context, repoPath string, ref string, treePath string) (*types.Submodule, error)
|
||||||
GetBlob(ctx context.Context, repoPath string, sha string, sizeLimit int64) (*types.BlobReader, error)
|
GetBlob(ctx context.Context, repoPath string, sha string, sizeLimit int64) (*types.BlobReader, error)
|
||||||
WalkReferences(ctx context.Context, repoPath string, handler types.WalkReferencesHandler,
|
WalkReferences(ctx context.Context, repoPath string, handler types.WalkReferencesHandler,
|
||||||
|
@ -55,17 +55,17 @@ func (s *CommitFilesService) CommitFiles(stream rpc.CommitFilesService_CommitFil
|
|||||||
ctx := stream.Context()
|
ctx := stream.Context()
|
||||||
headerRequest, err := stream.Recv()
|
headerRequest, err := stream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ErrInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
header := headerRequest.GetHeader()
|
header := headerRequest.GetHeader()
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return types.ErrHeaderCannotBeEmpty
|
return ErrInvalidArgument(types.ErrHeaderCannotBeEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
base := header.GetBase()
|
base := header.GetBase()
|
||||||
if base == nil {
|
if base == nil {
|
||||||
return types.ErrBaseCannotBeEmpty
|
return ErrInvalidArgument(types.ErrBaseCannotBeEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
committer := base.GetActor()
|
committer := base.GetActor()
|
||||||
@ -101,7 +101,7 @@ func (s *CommitFilesService) CommitFiles(stream rpc.CommitFilesService_CommitFil
|
|||||||
// If the user wants to actually build a disconnected commit graph they can use the cli.
|
// If the user wants to actually build a disconnected commit graph they can use the cli.
|
||||||
isEmpty, err := repoHasBranches(ctx, repo)
|
isEmpty, err := repoHasBranches(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to determine if repo is empty: %w", err)
|
return ErrInternalf("failed to determine if repository is empty", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure input data is valid
|
// ensure input data is valid
|
||||||
@ -118,7 +118,7 @@ func (s *CommitFilesService) CommitFiles(stream rpc.CommitFilesService_CommitFil
|
|||||||
// create a new shared repo
|
// create a new shared repo
|
||||||
shared, err := NewSharedRepo(s.reposTempDir, base.GetRepoUid(), repo)
|
shared, err := NewSharedRepo(s.reposTempDir, base.GetRepoUid(), repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to create shared repository")
|
||||||
}
|
}
|
||||||
defer shared.Close(ctx)
|
defer shared.Close(ctx)
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ func (s *CommitFilesService) CommitFiles(stream rpc.CommitFilesService_CommitFil
|
|||||||
// Now write the tree
|
// Now write the tree
|
||||||
treeHash, err := shared.WriteTree(ctx)
|
treeHash, err := shared.WriteTree(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to write tree object")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := strings.TrimSpace(header.GetTitle())
|
message := strings.TrimSpace(header.GetTitle())
|
||||||
@ -159,16 +159,16 @@ func (s *CommitFilesService) CommitFiles(stream rpc.CommitFilesService_CommitFil
|
|||||||
committerDate,
|
committerDate,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to commit the tree")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = shared.PushCommitToBranch(ctx, base, commitSHA, header.GetNewBranchName()); err != nil {
|
if err = shared.PushCommitToBranch(ctx, base, commitSHA, header.GetNewBranchName()); err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to push commits to remote repository")
|
||||||
}
|
}
|
||||||
|
|
||||||
commit, err := shared.GetCommit(commitSHA)
|
commit, err := shared.GetCommit(commitSHA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to get commit for SHA %s", commitSHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream.SendAndClose(&rpc.CommitFilesResponse{
|
return stream.SendAndClose(&rpc.CommitFilesResponse{
|
||||||
@ -186,7 +186,7 @@ func (s *CommitFilesService) prepareTree(ctx context.Context, shared *SharedRepo
|
|||||||
// Get the latest commit of the original branch
|
// Get the latest commit of the original branch
|
||||||
commit, err := shared.GetBranchCommit(branchName)
|
commit, err := shared.GetBranchCommit(branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", processGitErrorf(err, "failed to get latest commit of the branch %s", branchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute all actions
|
// execute all actions
|
||||||
@ -205,22 +205,22 @@ func (s *CommitFilesService) prepareTreeEmptyRepo(ctx context.Context, shared *S
|
|||||||
// init a new repo (full clone would cause risk that by time of push someone wrote to the remote repo!)
|
// init a new repo (full clone would cause risk that by time of push someone wrote to the remote repo!)
|
||||||
err := shared.Init(ctx)
|
err := shared.Init(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to init shared tmp repo: %w", err)
|
return processGitErrorf(err, "failed to init shared tmp repository")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if action.header.Action != rpc.CommitFilesActionHeader_CREATE {
|
if action.header.Action != rpc.CommitFilesActionHeader_CREATE {
|
||||||
return types.ErrActionNotAllowedOnEmptyRepo
|
return ErrFailedPrecondition(types.ErrActionNotAllowedOnEmptyRepo)
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := files.CleanUploadFileName(action.header.GetPath())
|
filePath := files.CleanUploadFileName(action.header.GetPath())
|
||||||
if filePath == "" {
|
if filePath == "" {
|
||||||
return types.ErrInvalidPath
|
return ErrInvalidArgument(types.ErrInvalidPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bytes.NewReader(action.content)
|
reader := bytes.NewReader(action.content)
|
||||||
if err = createFile(ctx, shared, nil, filePath, defaultFilePermission, reader); err != nil {
|
if err = createFile(ctx, shared, nil, filePath, defaultFilePermission, reader); err != nil {
|
||||||
return fmt.Errorf("failed to create file '%s': %w", action.header.Path, err)
|
return ErrInternalf("failed to create file '%s'", action.header.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ func (s *CommitFilesService) validateAndPrepareHeader(repo *git.Repository, isEm
|
|||||||
if header.GetBranchName() == "" {
|
if header.GetBranchName() == "" {
|
||||||
defaultBranchRef, err := repo.GetDefaultBranch()
|
defaultBranchRef, err := repo.GetDefaultBranch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to get default branch")
|
||||||
}
|
}
|
||||||
header.BranchName = defaultBranchRef
|
header.BranchName = defaultBranchRef
|
||||||
}
|
}
|
||||||
@ -252,17 +252,17 @@ func (s *CommitFilesService) validateAndPrepareHeader(repo *git.Repository, isEm
|
|||||||
|
|
||||||
// ensure source branch exists
|
// ensure source branch exists
|
||||||
if _, err := repo.GetBranch(header.GetBranchName()); err != nil {
|
if _, err := repo.GetBranch(header.GetBranchName()); err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed to get source branch %s", header.BranchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure new branch doesn't exist yet (if new branch creation was requested)
|
// ensure new branch doesn't exist yet (if new branch creation was requested)
|
||||||
if header.GetBranchName() != header.GetNewBranchName() {
|
if header.GetBranchName() != header.GetNewBranchName() {
|
||||||
existingBranch, err := repo.GetBranch(header.GetNewBranchName())
|
existingBranch, err := repo.GetBranch(header.GetNewBranchName())
|
||||||
if existingBranch != nil {
|
if existingBranch != nil {
|
||||||
return fmt.Errorf("branch %s %w", existingBranch.Name, types.ErrAlreadyExists)
|
return ErrAlreadyExistsf("branch %s already exists", existingBranch.Name)
|
||||||
}
|
}
|
||||||
if err != nil && !git.IsErrBranchNotExist(err) {
|
if err != nil && !git.IsErrBranchNotExist(err) {
|
||||||
return err
|
return processGitErrorf(err, "failed to create new branch %s", header.NewBranchName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -274,11 +274,11 @@ func (s *CommitFilesService) clone(
|
|||||||
branch string,
|
branch string,
|
||||||
) error {
|
) error {
|
||||||
if err := shared.Clone(ctx, branch); err != nil {
|
if err := shared.Clone(ctx, branch); err != nil {
|
||||||
return fmt.Errorf("failed to clone branch '%s': %w", branch, err)
|
return ErrInternalf("failed to clone branch '%s'", branch, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := shared.SetDefaultIndex(ctx); err != nil {
|
if err := shared.SetDefaultIndex(ctx); err != nil {
|
||||||
return fmt.Errorf("failed to set default index: %w", err)
|
return ErrInternalf("failed to set default index", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -299,7 +299,7 @@ func (s *CommitFilesService) collectActions(
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("receive request: %w", err)
|
return ErrInternalf("receive request failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch payload := req.GetAction().GetPayload().(type) {
|
switch payload := req.GetAction().GetPayload().(type) {
|
||||||
@ -307,18 +307,18 @@ func (s *CommitFilesService) collectActions(
|
|||||||
actions = append(actions, fileAction{header: payload.Header})
|
actions = append(actions, fileAction{header: payload.Header})
|
||||||
case *rpc.CommitFilesAction_Content:
|
case *rpc.CommitFilesAction_Content:
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
return types.ErrContentSentBeforeAction
|
return ErrFailedPrecondition(types.ErrContentSentBeforeAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the content to the previous fileAction
|
// append the content to the previous fileAction
|
||||||
content := &actions[len(actions)-1].content
|
content := &actions[len(actions)-1].content
|
||||||
*content = append(*content, payload.Content...)
|
*content = append(*content, payload.Content...)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unhandled fileAction payload type: %T", payload)
|
return ErrInternalf("unhandled fileAction payload type: %T", payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
return types.ErrActionListEmpty
|
return ErrInvalidArgument(types.ErrActionListEmpty)
|
||||||
}
|
}
|
||||||
*ptrActions = actions
|
*ptrActions = actions
|
||||||
return nil
|
return nil
|
||||||
@ -330,20 +330,14 @@ func (s *CommitFilesService) processAction(
|
|||||||
action *fileAction,
|
action *fileAction,
|
||||||
commit *git.Commit,
|
commit *git.Commit,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("in processActions: %w", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
header := action.header
|
header := action.header
|
||||||
if _, ok := rpc.CommitFilesActionHeader_ActionType_name[int32(header.Action)]; !ok {
|
if _, ok := rpc.CommitFilesActionHeader_ActionType_name[int32(header.Action)]; !ok {
|
||||||
return fmt.Errorf("%s %w", action.header.Action, types.ErrUndefinedAction)
|
return ErrInvalidArgumentf("undefined file action %s", action.header.Action, types.ErrUndefinedAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := files.CleanUploadFileName(header.GetPath())
|
filePath := files.CleanUploadFileName(header.GetPath())
|
||||||
if filePath == "" {
|
if filePath == "" {
|
||||||
return types.ErrInvalidPath
|
return ErrInvalidArgument(types.ErrInvalidPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bytes.NewReader(action.content)
|
reader := bytes.NewReader(action.content)
|
||||||
@ -373,12 +367,12 @@ func createFile(ctx context.Context, repo *SharedRepo, commit *git.Commit,
|
|||||||
|
|
||||||
hash, err := repo.WriteGitObject(ctx, reader)
|
hash, err := repo.WriteGitObject(ctx, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error hashing object %w", err)
|
return processGitErrorf(err, "error hashing object")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the object to the index
|
// Add the object to the index
|
||||||
if err = repo.AddObjectToIndex(ctx, mode, hash, filePath); err != nil {
|
if err = repo.AddObjectToIndex(ctx, mode, hash, filePath); err != nil {
|
||||||
return fmt.Errorf("error creating object: %w", err)
|
return processGitErrorf(err, "error creating object")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -388,7 +382,7 @@ func updateFile(ctx context.Context, repo *SharedRepo, commit *git.Commit, fileP
|
|||||||
// get file mode from existing file (default unless executable)
|
// get file mode from existing file (default unless executable)
|
||||||
entry, err := getFileEntry(commit, sha, filePath)
|
entry, err := getFileEntry(commit, sha, filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get file entry: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
if entry.IsExecutable() {
|
if entry.IsExecutable() {
|
||||||
mode = "100755"
|
mode = "100755"
|
||||||
@ -396,11 +390,11 @@ func updateFile(ctx context.Context, repo *SharedRepo, commit *git.Commit, fileP
|
|||||||
|
|
||||||
hash, err := repo.WriteGitObject(ctx, reader)
|
hash, err := repo.WriteGitObject(ctx, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error hashing object %w", err)
|
return processGitErrorf(err, "error hashing object")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.AddObjectToIndex(ctx, mode, hash, filePath); err != nil {
|
if err = repo.AddObjectToIndex(ctx, mode, hash, filePath); err != nil {
|
||||||
return fmt.Errorf("error updating object: %w", err)
|
return processGitErrorf(err, "error updating object")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -416,7 +410,7 @@ func moveFile(ctx context.Context, repo *SharedRepo, commit *git.Commit,
|
|||||||
if buffer.Len() == 0 && newPath != "" {
|
if buffer.Len() == 0 && newPath != "" {
|
||||||
err = repo.ShowFile(ctx, filePath, commit.ID.String(), buffer)
|
err = repo.ShowFile(ctx, filePath, commit.ID.String(), buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return processGitErrorf(err, "failed lookup for path %s", newPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,23 +420,23 @@ func moveFile(ctx context.Context, repo *SharedRepo, commit *git.Commit,
|
|||||||
|
|
||||||
filesInIndex, err := repo.LsFiles(ctx, filePath)
|
filesInIndex, err := repo.LsFiles(ctx, filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listing files error %w", err)
|
return processGitErrorf(err, "listing files error")
|
||||||
}
|
}
|
||||||
if !slices.Contains(filesInIndex, filePath) {
|
if !slices.Contains(filesInIndex, filePath) {
|
||||||
return fmt.Errorf("%s %w", filePath, types.ErrNotFound)
|
return ErrNotFoundf("path %s not found", filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := repo.WriteGitObject(ctx, buffer)
|
hash, err := repo.WriteGitObject(ctx, buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error hashing object %w", err)
|
return processGitErrorf(err, "error hashing object")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.AddObjectToIndex(ctx, mode, hash, newPath); err != nil {
|
if err = repo.AddObjectToIndex(ctx, mode, hash, newPath); err != nil {
|
||||||
return fmt.Errorf("add object: %w", err)
|
return processGitErrorf(err, "add object error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.RemoveFilesFromIndex(ctx, filePath); err != nil {
|
if err = repo.RemoveFilesFromIndex(ctx, filePath); err != nil {
|
||||||
return fmt.Errorf("remove object: %w", err)
|
return processGitErrorf(err, "remove object error")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -450,14 +444,14 @@ func moveFile(ctx context.Context, repo *SharedRepo, commit *git.Commit,
|
|||||||
func deleteFile(ctx context.Context, repo *SharedRepo, filePath string) error {
|
func deleteFile(ctx context.Context, repo *SharedRepo, filePath string) error {
|
||||||
filesInIndex, err := repo.LsFiles(ctx, filePath)
|
filesInIndex, err := repo.LsFiles(ctx, filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listing files error %w", err)
|
return processGitErrorf(err, "listing files error")
|
||||||
}
|
}
|
||||||
if !slices.Contains(filesInIndex, filePath) {
|
if !slices.Contains(filesInIndex, filePath) {
|
||||||
return fmt.Errorf("%s %w", filePath, types.ErrNotFound)
|
return ErrNotFoundf("file path %s not found", filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo.RemoveFilesFromIndex(ctx, filePath); err != nil {
|
if err = repo.RemoveFilesFromIndex(ctx, filePath); err != nil {
|
||||||
return fmt.Errorf("remove object: %w", err)
|
return processGitErrorf(err, "remove object error")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -469,16 +463,16 @@ func getFileEntry(
|
|||||||
) (*git.TreeEntry, error) {
|
) (*git.TreeEntry, error) {
|
||||||
entry, err := commit.GetTreeEntryByPath(path)
|
entry, err := commit.GetTreeEntryByPath(path)
|
||||||
if git.IsErrNotExist(err) {
|
if git.IsErrNotExist(err) {
|
||||||
return nil, fmt.Errorf("%s %w", path, types.ErrNotFound)
|
return nil, ErrNotFoundf("path %s not found", path)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, processGitErrorf(err, "failed to get tree for path %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
|
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
|
||||||
if sha == "" || sha != entry.ID.String() {
|
if sha == "" || sha != entry.ID.String() {
|
||||||
return nil, fmt.Errorf("%w for path %s [given: %s, expected: %s]",
|
return nil, ErrInvalidArgumentf("sha does not match for path %s [given: %s, expected: %s]",
|
||||||
types.ErrSHADoesNotMatch, path, sha, entry.ID.String())
|
path, sha, entry.ID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry, nil
|
return entry, nil
|
||||||
@ -500,22 +494,22 @@ func checkPathAvailability(commit *git.Commit, filePath string, isNewFile bool)
|
|||||||
// Means there is no item with that name, so we're good
|
// Means there is no item with that name, so we're good
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return err
|
return processGitErrorf(err, "failed to get tree entry for path %s", subTreePath)
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case index < len(parts)-1:
|
case index < len(parts)-1:
|
||||||
if !entry.IsDir() {
|
if !entry.IsDir() {
|
||||||
return fmt.Errorf("a file %w where you're trying to create a subdirectory [path: %s]",
|
return ErrAlreadyExistsf("a file already exists where you're trying to create a subdirectory [path: %s]",
|
||||||
types.ErrAlreadyExists, subTreePath)
|
subTreePath)
|
||||||
}
|
}
|
||||||
case entry.IsLink():
|
case entry.IsLink():
|
||||||
return fmt.Errorf("a symbolic link %w where you're trying to create a subdirectory [path: %s]",
|
return fmt.Errorf("a symbolic link %w where you're trying to create a subdirectory [path: %s]",
|
||||||
types.ErrAlreadyExists, subTreePath)
|
types.ErrAlreadyExists, subTreePath)
|
||||||
case entry.IsDir():
|
case entry.IsDir():
|
||||||
return fmt.Errorf("a directory %w where you're trying to create a subdirectory [path: %s]",
|
return ErrAlreadyExistsf("a directory already exists where you're trying to create a subdirectory [path: %s]",
|
||||||
types.ErrAlreadyExists, subTreePath)
|
subTreePath)
|
||||||
case filePath != "" || isNewFile:
|
case filePath != "" || isNewFile:
|
||||||
return fmt.Errorf("%s %w", filePath, types.ErrAlreadyExists)
|
return ErrAlreadyExistsf("file path %s already exists", filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -530,7 +524,7 @@ func repoHasBranches(ctx context.Context, repo *git.Repository) (bool, error) {
|
|||||||
stdout, _, runErr := git.NewCommand(ctx, "rev-list", "--max-count", "1", "--branches").
|
stdout, _, runErr := git.NewCommand(ctx, "rev-list", "--max-count", "1", "--branches").
|
||||||
RunStdBytes(&git.RunOpts{Dir: repo.Path})
|
RunStdBytes(&git.RunOpts{Dir: repo.Path})
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
return false, fmt.Errorf("failed to trigger rev-list command: %w", runErr)
|
return false, processGitErrorf(runErr, "failed to trigger rev-list command")
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSpace(string(stdout)) == "", nil
|
return strings.TrimSpace(string(stdout)) == "", nil
|
||||||
|
@ -6,6 +6,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/harness/gitness/gitrpc/internal/types"
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
"github.com/harness/gitness/gitrpc/rpc"
|
"github.com/harness/gitness/gitrpc/rpc"
|
||||||
@ -15,8 +16,10 @@ import (
|
|||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
|
func (s RepositoryService) ListTreeNodes(
|
||||||
stream rpc.RepositoryService_ListTreeNodesServer) error {
|
request *rpc.ListTreeNodesRequest,
|
||||||
|
stream rpc.RepositoryService_ListTreeNodesServer,
|
||||||
|
) error {
|
||||||
ctx := stream.Context()
|
ctx := stream.Context()
|
||||||
base := request.GetBase()
|
base := request.GetBase()
|
||||||
if base == nil {
|
if base == nil {
|
||||||
@ -26,7 +29,7 @@ func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
|
|||||||
repoPath := getFullPathForRepo(s.reposRoot, base.GetRepoUid())
|
repoPath := getFullPathForRepo(s.reposRoot, base.GetRepoUid())
|
||||||
|
|
||||||
gitNodes, err := s.adapter.ListTreeNodes(ctx, repoPath,
|
gitNodes, err := s.adapter.ListTreeNodes(ctx, repoPath,
|
||||||
request.GetGitRef(), request.GetPath(), request.GetRecursive(), request.GetIncludeLatestCommit())
|
request.GetGitRef(), request.GetPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return processGitErrorf(err, "failed to list tree nodes")
|
return processGitErrorf(err, "failed to list tree nodes")
|
||||||
}
|
}
|
||||||
@ -34,14 +37,6 @@ func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
|
|||||||
log.Ctx(ctx).Trace().Msgf("git adapter returned %d nodes", len(gitNodes))
|
log.Ctx(ctx).Trace().Msgf("git adapter returned %d nodes", len(gitNodes))
|
||||||
|
|
||||||
for _, gitNode := range gitNodes {
|
for _, gitNode := range gitNodes {
|
||||||
var commit *rpc.Commit
|
|
||||||
if request.GetIncludeLatestCommit() {
|
|
||||||
commit, err = mapGitCommit(gitNode.Commit)
|
|
||||||
if err != nil {
|
|
||||||
return status.Errorf(codes.Internal, "failed to map git commit: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stream.Send(&rpc.ListTreeNodesResponse{
|
err = stream.Send(&rpc.ListTreeNodesResponse{
|
||||||
Node: &rpc.TreeNode{
|
Node: &rpc.TreeNode{
|
||||||
Type: mapGitNodeType(gitNode.NodeType),
|
Type: mapGitNodeType(gitNode.NodeType),
|
||||||
@ -50,7 +45,6 @@ func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
|
|||||||
Name: gitNode.Name,
|
Name: gitNode.Name,
|
||||||
Path: gitNode.Path,
|
Path: gitNode.Path,
|
||||||
},
|
},
|
||||||
Commit: commit,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(codes.Internal, "failed to send node: %v", err)
|
return status.Errorf(codes.Internal, "failed to send node: %v", err)
|
||||||
@ -61,15 +55,16 @@ func (s RepositoryService) ListTreeNodes(request *rpc.ListTreeNodesRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s RepositoryService) GetTreeNode(ctx context.Context,
|
func (s RepositoryService) GetTreeNode(ctx context.Context,
|
||||||
request *rpc.GetTreeNodeRequest) (*rpc.GetTreeNodeResponse, error) {
|
request *rpc.GetTreeNodeRequest,
|
||||||
|
) (*rpc.GetTreeNodeResponse, error) {
|
||||||
base := request.GetBase()
|
base := request.GetBase()
|
||||||
if base == nil {
|
if base == nil {
|
||||||
return nil, types.ErrBaseCannotBeEmpty
|
return nil, types.ErrBaseCannotBeEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
repoPath := getFullPathForRepo(s.reposRoot, base.GetRepoUid())
|
repoPath := getFullPathForRepo(s.reposRoot, base.GetRepoUid())
|
||||||
// TODO: do we need to validate request for nil?
|
|
||||||
gitNode, err := s.adapter.GetTreeNode(ctx, repoPath, request.GetGitRef(), request.GetPath())
|
gitNode, err := s.adapter.GetTreeNode(ctx, repoPath, request.GitRef, request.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processGitErrorf(err, "no such path '%s' in '%s'", request.Path, request.GetGitRef())
|
return nil, processGitErrorf(err, "no such path '%s' in '%s'", request.Path, request.GetGitRef())
|
||||||
}
|
}
|
||||||
@ -84,15 +79,61 @@ func (s RepositoryService) GetTreeNode(ctx context.Context,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: improve performance, could be done in lower layer?
|
|
||||||
if request.GetIncludeLatestCommit() {
|
if request.GetIncludeLatestCommit() {
|
||||||
var commit *rpc.Commit
|
pathDetails, err := s.adapter.PathsDetails(ctx, repoPath, request.GitRef, []string{request.Path})
|
||||||
commit, err = s.getLatestCommit(ctx, repoPath, request.GetGitRef(), request.GetPath())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res.Commit = commit
|
|
||||||
|
if len(pathDetails) != 1 {
|
||||||
|
return nil, fmt.Errorf("failed to get details for the path %s", request.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pathDetails[0].LastCommit != nil {
|
||||||
|
res.Commit, err = mapGitCommit(pathDetails[0].LastCommit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s RepositoryService) PathsDetails(ctx context.Context,
|
||||||
|
request *rpc.PathsDetailsRequest,
|
||||||
|
) (*rpc.PathsDetailsResponse, error) {
|
||||||
|
base := request.GetBase()
|
||||||
|
if base == nil {
|
||||||
|
return nil, types.ErrBaseCannotBeEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPath := getFullPathForRepo(s.reposRoot, base.GetRepoUid())
|
||||||
|
|
||||||
|
pathsDetails, err := s.adapter.PathsDetails(ctx, repoPath, request.GetGitRef(), request.GetPaths())
|
||||||
|
if err != nil {
|
||||||
|
return nil, processGitErrorf(err, "failed to get path details in '%s'", request.GetGitRef())
|
||||||
|
}
|
||||||
|
|
||||||
|
details := make([]*rpc.PathDetails, len(pathsDetails))
|
||||||
|
for i, pathDetails := range pathsDetails {
|
||||||
|
var lastCommit *rpc.Commit
|
||||||
|
|
||||||
|
if pathDetails.LastCommit != nil {
|
||||||
|
lastCommit, err = mapGitCommit(pathDetails.LastCommit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to map commit: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
details[i] = &rpc.PathDetails{
|
||||||
|
Path: pathDetails.Path,
|
||||||
|
LastCommit: lastCommit,
|
||||||
|
Size: pathDetails.Size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rpc.PathsDetailsResponse{
|
||||||
|
PathDetails: details,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
ErrAlreadyExists = errors.New("already exists")
|
ErrAlreadyExists = errors.New("already exists")
|
||||||
ErrInvalidArgument = errors.New("invalid argument")
|
ErrInvalidArgument = errors.New("invalid argument")
|
||||||
ErrNotFound = errors.New("not found")
|
ErrNotFound = errors.New("not found")
|
||||||
|
ErrPathNotFound = errors.New("path not found")
|
||||||
ErrInvalidPath = errors.New("path is invalid")
|
ErrInvalidPath = errors.New("path is invalid")
|
||||||
ErrUndefinedAction = errors.New("undefined action")
|
ErrUndefinedAction = errors.New("undefined action")
|
||||||
ErrActionNotAllowedOnEmptyRepo = errors.New("action not allowed on empty repository")
|
ErrActionNotAllowedOnEmptyRepo = errors.New("action not allowed on empty repository")
|
||||||
|
@ -320,3 +320,9 @@ type TempRepository struct {
|
|||||||
BaseSHA string
|
BaseSHA string
|
||||||
HeadSHA string
|
HeadSHA string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PathDetails struct {
|
||||||
|
Path string
|
||||||
|
LastCommit *Commit
|
||||||
|
Size int64
|
||||||
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -66,7 +65,7 @@ type CommitFilesResponse struct {
|
|||||||
func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (CommitFilesResponse, error) {
|
func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (CommitFilesResponse, error) {
|
||||||
stream, err := c.commitFilesService.CommitFiles(ctx)
|
stream, err := c.commitFilesService.CommitFiles(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CommitFilesResponse{}, err
|
return CommitFilesResponse{}, processRPCErrorf(err, "failed to open file stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = stream.Send(&rpc.CommitFilesRequest{
|
if err = stream.Send(&rpc.CommitFilesRequest{
|
||||||
@ -84,7 +83,7 @@ func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (Co
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return CommitFilesResponse{}, err
|
return CommitFilesResponse{}, processRPCErrorf(err, "failed to send file headers")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, action := range params.Actions {
|
for _, action := range params.Actions {
|
||||||
@ -103,7 +102,7 @@ func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (Co
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return CommitFilesResponse{}, err
|
return CommitFilesResponse{}, processRPCErrorf(err, "failed to send file action to the stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
// send file content
|
// send file content
|
||||||
@ -115,7 +114,7 @@ func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (Co
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CommitFilesResponse{}, fmt.Errorf("cannot read buffer: %w", err)
|
return CommitFilesResponse{}, processRPCErrorf(err, "cannot read buffer")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = stream.Send(&rpc.CommitFilesRequest{
|
if err = stream.Send(&rpc.CommitFilesRequest{
|
||||||
@ -127,14 +126,14 @@ func (c *Client) CommitFiles(ctx context.Context, params *CommitFilesParams) (Co
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return CommitFilesResponse{}, err
|
return CommitFilesResponse{}, processRPCErrorf(err, "failed to send file to the stream")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recv, err := stream.CloseAndRecv()
|
recv, err := stream.CloseAndRecv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CommitFilesResponse{}, err
|
return CommitFilesResponse{}, processRPCErrorf(err, "failed to close the stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommitFilesResponse{
|
return CommitFilesResponse{
|
||||||
|
@ -10,6 +10,7 @@ service RepositoryService {
|
|||||||
rpc CreateRepository(stream CreateRepositoryRequest) returns (CreateRepositoryResponse);
|
rpc CreateRepository(stream CreateRepositoryRequest) returns (CreateRepositoryResponse);
|
||||||
rpc GetTreeNode(GetTreeNodeRequest) returns (GetTreeNodeResponse);
|
rpc GetTreeNode(GetTreeNodeRequest) returns (GetTreeNodeResponse);
|
||||||
rpc ListTreeNodes(ListTreeNodesRequest) returns (stream ListTreeNodesResponse);
|
rpc ListTreeNodes(ListTreeNodesRequest) returns (stream ListTreeNodesResponse);
|
||||||
|
rpc PathsDetails(PathsDetailsRequest) returns (PathsDetailsResponse);
|
||||||
rpc GetSubmodule(GetSubmoduleRequest) returns (GetSubmoduleResponse);
|
rpc GetSubmodule(GetSubmoduleRequest) returns (GetSubmoduleResponse);
|
||||||
rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse);
|
rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse);
|
||||||
rpc ListCommits(ListCommitsRequest) returns (stream ListCommitsResponse);
|
rpc ListCommits(ListCommitsRequest) returns (stream ListCommitsResponse);
|
||||||
@ -55,13 +56,10 @@ message ListTreeNodesRequest {
|
|||||||
ReadRequest base = 1;
|
ReadRequest base = 1;
|
||||||
string git_ref = 2;
|
string git_ref = 2;
|
||||||
string path = 3;
|
string path = 3;
|
||||||
bool include_latest_commit = 4;
|
|
||||||
bool recursive = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListTreeNodesResponse {
|
message ListTreeNodesResponse {
|
||||||
TreeNode node = 1;
|
TreeNode node = 1;
|
||||||
Commit commit = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TreeNode {
|
message TreeNode {
|
||||||
@ -86,6 +84,22 @@ enum TreeNodeMode {
|
|||||||
TreeNodeModeCommit = 4;
|
TreeNodeModeCommit = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message PathsDetailsRequest {
|
||||||
|
ReadRequest base = 1;
|
||||||
|
string git_ref = 2;
|
||||||
|
repeated string paths = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PathsDetailsResponse {
|
||||||
|
repeated PathDetails path_details = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PathDetails {
|
||||||
|
string path = 1;
|
||||||
|
Commit last_commit = 2;
|
||||||
|
int64 size = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message GetCommitRequest {
|
message GetCommitRequest {
|
||||||
ReadRequest base = 1;
|
ReadRequest base = 1;
|
||||||
string sha = 2;
|
string sha = 2;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ type RepositoryServiceClient interface {
|
|||||||
CreateRepository(ctx context.Context, opts ...grpc.CallOption) (RepositoryService_CreateRepositoryClient, error)
|
CreateRepository(ctx context.Context, opts ...grpc.CallOption) (RepositoryService_CreateRepositoryClient, error)
|
||||||
GetTreeNode(ctx context.Context, in *GetTreeNodeRequest, opts ...grpc.CallOption) (*GetTreeNodeResponse, error)
|
GetTreeNode(ctx context.Context, in *GetTreeNodeRequest, opts ...grpc.CallOption) (*GetTreeNodeResponse, error)
|
||||||
ListTreeNodes(ctx context.Context, in *ListTreeNodesRequest, opts ...grpc.CallOption) (RepositoryService_ListTreeNodesClient, error)
|
ListTreeNodes(ctx context.Context, in *ListTreeNodesRequest, opts ...grpc.CallOption) (RepositoryService_ListTreeNodesClient, error)
|
||||||
|
PathsDetails(ctx context.Context, in *PathsDetailsRequest, opts ...grpc.CallOption) (*PathsDetailsResponse, error)
|
||||||
GetSubmodule(ctx context.Context, in *GetSubmoduleRequest, opts ...grpc.CallOption) (*GetSubmoduleResponse, error)
|
GetSubmodule(ctx context.Context, in *GetSubmoduleRequest, opts ...grpc.CallOption) (*GetSubmoduleResponse, error)
|
||||||
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (RepositoryService_GetBlobClient, error)
|
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (RepositoryService_GetBlobClient, error)
|
||||||
ListCommits(ctx context.Context, in *ListCommitsRequest, opts ...grpc.CallOption) (RepositoryService_ListCommitsClient, error)
|
ListCommits(ctx context.Context, in *ListCommitsRequest, opts ...grpc.CallOption) (RepositoryService_ListCommitsClient, error)
|
||||||
@ -119,6 +120,15 @@ func (x *repositoryServiceListTreeNodesClient) Recv() (*ListTreeNodesResponse, e
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *repositoryServiceClient) PathsDetails(ctx context.Context, in *PathsDetailsRequest, opts ...grpc.CallOption) (*PathsDetailsResponse, error) {
|
||||||
|
out := new(PathsDetailsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/rpc.RepositoryService/PathsDetails", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *repositoryServiceClient) GetSubmodule(ctx context.Context, in *GetSubmoduleRequest, opts ...grpc.CallOption) (*GetSubmoduleResponse, error) {
|
func (c *repositoryServiceClient) GetSubmodule(ctx context.Context, in *GetSubmoduleRequest, opts ...grpc.CallOption) (*GetSubmoduleResponse, error) {
|
||||||
out := new(GetSubmoduleResponse)
|
out := new(GetSubmoduleResponse)
|
||||||
err := c.cc.Invoke(ctx, "/rpc.RepositoryService/GetSubmodule", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/rpc.RepositoryService/GetSubmodule", in, out, opts...)
|
||||||
@ -253,6 +263,7 @@ type RepositoryServiceServer interface {
|
|||||||
CreateRepository(RepositoryService_CreateRepositoryServer) error
|
CreateRepository(RepositoryService_CreateRepositoryServer) error
|
||||||
GetTreeNode(context.Context, *GetTreeNodeRequest) (*GetTreeNodeResponse, error)
|
GetTreeNode(context.Context, *GetTreeNodeRequest) (*GetTreeNodeResponse, error)
|
||||||
ListTreeNodes(*ListTreeNodesRequest, RepositoryService_ListTreeNodesServer) error
|
ListTreeNodes(*ListTreeNodesRequest, RepositoryService_ListTreeNodesServer) error
|
||||||
|
PathsDetails(context.Context, *PathsDetailsRequest) (*PathsDetailsResponse, error)
|
||||||
GetSubmodule(context.Context, *GetSubmoduleRequest) (*GetSubmoduleResponse, error)
|
GetSubmodule(context.Context, *GetSubmoduleRequest) (*GetSubmoduleResponse, error)
|
||||||
GetBlob(*GetBlobRequest, RepositoryService_GetBlobServer) error
|
GetBlob(*GetBlobRequest, RepositoryService_GetBlobServer) error
|
||||||
ListCommits(*ListCommitsRequest, RepositoryService_ListCommitsServer) error
|
ListCommits(*ListCommitsRequest, RepositoryService_ListCommitsServer) error
|
||||||
@ -278,6 +289,9 @@ func (UnimplementedRepositoryServiceServer) GetTreeNode(context.Context, *GetTre
|
|||||||
func (UnimplementedRepositoryServiceServer) ListTreeNodes(*ListTreeNodesRequest, RepositoryService_ListTreeNodesServer) error {
|
func (UnimplementedRepositoryServiceServer) ListTreeNodes(*ListTreeNodesRequest, RepositoryService_ListTreeNodesServer) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method ListTreeNodes not implemented")
|
return status.Errorf(codes.Unimplemented, "method ListTreeNodes not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedRepositoryServiceServer) PathsDetails(context.Context, *PathsDetailsRequest) (*PathsDetailsResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method PathsDetails not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedRepositoryServiceServer) GetSubmodule(context.Context, *GetSubmoduleRequest) (*GetSubmoduleResponse, error) {
|
func (UnimplementedRepositoryServiceServer) GetSubmodule(context.Context, *GetSubmoduleRequest) (*GetSubmoduleResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetSubmodule not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetSubmodule not implemented")
|
||||||
}
|
}
|
||||||
@ -383,6 +397,24 @@ func (x *repositoryServiceListTreeNodesServer) Send(m *ListTreeNodesResponse) er
|
|||||||
return x.ServerStream.SendMsg(m)
|
return x.ServerStream.SendMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _RepositoryService_PathsDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PathsDetailsRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RepositoryServiceServer).PathsDetails(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/rpc.RepositoryService/PathsDetails",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RepositoryServiceServer).PathsDetails(ctx, req.(*PathsDetailsRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _RepositoryService_GetSubmodule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _RepositoryService_GetSubmodule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(GetSubmoduleRequest)
|
in := new(GetSubmoduleRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@ -562,6 +594,10 @@ var RepositoryService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "GetTreeNode",
|
MethodName: "GetTreeNode",
|
||||||
Handler: _RepositoryService_GetTreeNode_Handler,
|
Handler: _RepositoryService_GetTreeNode_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "PathsDetails",
|
||||||
|
Handler: _RepositoryService_PathsDetails_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "GetSubmodule",
|
MethodName: "GetSubmodule",
|
||||||
Handler: _RepositoryService_GetSubmodule_Handler,
|
Handler: _RepositoryService_GetSubmodule_Handler,
|
||||||
|
@ -9,6 +9,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModeInMemory = "inmemory"
|
||||||
|
ModeRedis = "redis"
|
||||||
|
ModeNone = "none"
|
||||||
|
)
|
||||||
|
|
||||||
// Config represents the configuration for the gitrpc server.
|
// Config represents the configuration for the gitrpc server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Bind specifies the addr used to bind the grpc server.
|
// Bind specifies the addr used to bind the grpc server.
|
||||||
@ -25,6 +31,25 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
MaxConnAge time.Duration `envconfig:"GITRPC_SERVER_MAX_CONN_AGE" default:"630720000s"`
|
MaxConnAge time.Duration `envconfig:"GITRPC_SERVER_MAX_CONN_AGE" default:"630720000s"`
|
||||||
MaxConnAgeGrace time.Duration `envconfig:"GITRPC_SERVER_MAX_CONN_AGE_GRACE" default:"630720000s"`
|
MaxConnAgeGrace time.Duration `envconfig:"GITRPC_SERVER_MAX_CONN_AGE_GRACE" default:"630720000s"`
|
||||||
|
|
||||||
|
// LastCommitCache holds configuration options for the last commit cache.
|
||||||
|
LastCommitCache struct {
|
||||||
|
// Mode determines where the cache will be. Valid values are "inmemory" (default), "redis" or "none".
|
||||||
|
Mode string `envconfig:"GITRPC_LAST_COMMIT_CACHE_MODE" default:"inmemory"`
|
||||||
|
|
||||||
|
// DurationSeconds defines cache duration in seconds of last commit, default=12h.
|
||||||
|
DurationSeconds int `envconfig:"GITRPC_LAST_COMMIT_CACHE_SECONDS" default:"43200"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Redis struct {
|
||||||
|
Endpoint string `envconfig:"GITRPC_REDIS_ENDPOINT" default:"localhost:6379"`
|
||||||
|
MaxRetries int `envconfig:"GITRPC_REDIS_MAX_RETRIES" default:"3"`
|
||||||
|
MinIdleConnections int `envconfig:"GITRPC_REDIS_MIN_IDLE_CONNECTIONS" default:"0"`
|
||||||
|
Password string `envconfig:"GITRPC_REDIS_PASSWORD"`
|
||||||
|
SentinelMode bool `envconfig:"GITRPC_REDIS_USE_SENTINEL" default:"false"`
|
||||||
|
SentinelMaster string `envconfig:"GITRPC_REDIS_SENTINEL_MASTER"`
|
||||||
|
SentinelEndpoint string `envconfig:"GITRPC_REDIS_SENTINEL_ENDPOINT"`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
@ -46,6 +71,9 @@ func (c *Config) Validate() error {
|
|||||||
if c.MaxConnAgeGrace == 0 {
|
if c.MaxConnAgeGrace == 0 {
|
||||||
return errors.New("config.MaxConnAgeGrace is required")
|
return errors.New("config.MaxConnAgeGrace is required")
|
||||||
}
|
}
|
||||||
|
if m := c.LastCommitCache.Mode; m != "" && m != ModeInMemory && m != ModeRedis && m != ModeNone {
|
||||||
|
return errors.New("config.LastCommitCache.Mode has unsupported value")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,14 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/cache"
|
||||||
"github.com/harness/gitness/gitrpc/internal/gitea"
|
"github.com/harness/gitness/gitrpc/internal/gitea"
|
||||||
"github.com/harness/gitness/gitrpc/internal/service"
|
"github.com/harness/gitness/gitrpc/internal/service"
|
||||||
|
"github.com/harness/gitness/gitrpc/internal/types"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,10 +21,37 @@ var WireSet = wire.NewSet(
|
|||||||
ProvideServer,
|
ProvideServer,
|
||||||
ProvideHTTPServer,
|
ProvideHTTPServer,
|
||||||
ProvideGITAdapter,
|
ProvideGITAdapter,
|
||||||
|
ProvideGoGitRepoCache,
|
||||||
|
ProvideLastCommitCache,
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideGITAdapter() (service.GitAdapter, error) {
|
func ProvideGoGitRepoCache() cache.Cache[string, *gitea.RepoEntryValue] {
|
||||||
return gitea.New()
|
return gitea.NewRepoCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideLastCommitCache(
|
||||||
|
config Config,
|
||||||
|
redisClient redis.UniversalClient,
|
||||||
|
repoCache cache.Cache[string, *gitea.RepoEntryValue],
|
||||||
|
) cache.Cache[gitea.CommitEntryKey, *types.Commit] {
|
||||||
|
cacheDuration := time.Duration(config.LastCommitCache.DurationSeconds) * time.Second
|
||||||
|
|
||||||
|
if config.LastCommitCache.Mode == ModeNone || cacheDuration < time.Second {
|
||||||
|
return gitea.NoLastCommitCache(repoCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LastCommitCache.Mode == ModeRedis && redisClient != nil {
|
||||||
|
return gitea.NewRedisLastCommitCache(redisClient, cacheDuration, repoCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gitea.NewInMemoryLastCommitCache(cacheDuration, repoCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideGITAdapter(
|
||||||
|
repoCache cache.Cache[string, *gitea.RepoEntryValue],
|
||||||
|
lastCommitCache cache.Cache[gitea.CommitEntryKey, *types.Commit],
|
||||||
|
) (service.GitAdapter, error) {
|
||||||
|
return gitea.New(repoCache, lastCommitCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideServer(config Config, adapter service.GitAdapter) (*GRPCServer, error) {
|
func ProvideServer(config Config, adapter service.GitAdapter) (*GRPCServer, error) {
|
||||||
|
@ -51,16 +51,10 @@ type ListTreeNodeParams struct {
|
|||||||
GitREF string
|
GitREF string
|
||||||
Path string
|
Path string
|
||||||
IncludeLatestCommit bool
|
IncludeLatestCommit bool
|
||||||
Recursive bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListTreeNodeOutput struct {
|
type ListTreeNodeOutput struct {
|
||||||
Nodes []TreeNodeWithCommit
|
Nodes []TreeNode
|
||||||
}
|
|
||||||
|
|
||||||
type TreeNodeWithCommit struct {
|
|
||||||
TreeNode
|
|
||||||
Commit *Commit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetTreeNodeParams struct {
|
type GetTreeNodeParams struct {
|
||||||
@ -94,6 +88,7 @@ func (c *Client) GetTreeNode(ctx context.Context, params *GetTreeNodeParams) (*G
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to map rpc node: %w", err)
|
return nil, fmt.Errorf("failed to map rpc node: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var commit *Commit
|
var commit *Commit
|
||||||
if resp.GetCommit() != nil {
|
if resp.GetCommit() != nil {
|
||||||
commit, err = mapRPCCommit(resp.GetCommit())
|
commit, err = mapRPCCommit(resp.GetCommit())
|
||||||
@ -113,17 +108,15 @@ func (c *Client) ListTreeNodes(ctx context.Context, params *ListTreeNodeParams)
|
|||||||
return nil, ErrNoParamsProvided
|
return nil, ErrNoParamsProvided
|
||||||
}
|
}
|
||||||
stream, err := c.repoService.ListTreeNodes(ctx, &rpc.ListTreeNodesRequest{
|
stream, err := c.repoService.ListTreeNodes(ctx, &rpc.ListTreeNodesRequest{
|
||||||
Base: mapToRPCReadRequest(params.ReadParams),
|
Base: mapToRPCReadRequest(params.ReadParams),
|
||||||
GitRef: params.GitREF,
|
GitRef: params.GitREF,
|
||||||
Path: params.Path,
|
Path: params.Path,
|
||||||
IncludeLatestCommit: params.IncludeLatestCommit,
|
|
||||||
Recursive: params.Recursive,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to start stream for tree nodes: %w", err)
|
return nil, fmt.Errorf("failed to start stream for tree nodes: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]TreeNodeWithCommit, 0, 16)
|
nodes := make([]TreeNode, 0, 16)
|
||||||
for {
|
for {
|
||||||
var next *rpc.ListTreeNodesResponse
|
var next *rpc.ListTreeNodesResponse
|
||||||
next, err = stream.Recv()
|
next, err = stream.Recv()
|
||||||
@ -140,21 +133,60 @@ func (c *Client) ListTreeNodes(ctx context.Context, params *ListTreeNodeParams)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to map rpc node: %w", err)
|
return nil, fmt.Errorf("failed to map rpc node: %w", err)
|
||||||
}
|
}
|
||||||
var commit *Commit
|
|
||||||
if next.GetCommit() != nil {
|
|
||||||
commit, err = mapRPCCommit(next.GetCommit())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to map rpc commit: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = append(nodes, TreeNodeWithCommit{
|
nodes = append(nodes, node)
|
||||||
TreeNode: node,
|
|
||||||
Commit: commit,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ListTreeNodeOutput{
|
return &ListTreeNodeOutput{
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PathsDetailsParams struct {
|
||||||
|
ReadParams
|
||||||
|
GitREF string
|
||||||
|
Paths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathsDetailsOutput struct {
|
||||||
|
Details []PathDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathDetails struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
LastCommit *Commit `json:"last_commit,omitempty"`
|
||||||
|
Size int64 `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) PathsDetails(ctx context.Context, params PathsDetailsParams) (PathsDetailsOutput, error) {
|
||||||
|
response, err := c.repoService.PathsDetails(ctx, &rpc.PathsDetailsRequest{
|
||||||
|
Base: mapToRPCReadRequest(params.ReadParams),
|
||||||
|
GitRef: params.GitREF,
|
||||||
|
Paths: params.Paths,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return PathsDetailsOutput{}, processRPCErrorf(err, "failed to get paths details")
|
||||||
|
}
|
||||||
|
|
||||||
|
details := make([]PathDetails, len(response.PathDetails))
|
||||||
|
for i, pathDetail := range response.PathDetails {
|
||||||
|
var lastCommit *Commit
|
||||||
|
|
||||||
|
if pathDetail.LastCommit != nil {
|
||||||
|
lastCommit, err = mapRPCCommit(pathDetail.LastCommit)
|
||||||
|
if err != nil {
|
||||||
|
return PathsDetailsOutput{}, fmt.Errorf("failed to map last commit: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
details[i] = PathDetails{
|
||||||
|
Path: pathDetail.Path,
|
||||||
|
Size: pathDetail.Size,
|
||||||
|
LastCommit: lastCommit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PathsDetailsOutput{
|
||||||
|
Details: details,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
29
internal/api/auth/pipeline.go
Normal file
29
internal/api/auth/pipeline.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckPipeline checks if a repo specific permission is granted for the current auth session
|
||||||
|
// in the scope of its parent.
|
||||||
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
|
func CheckPipeline(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||||
|
parentPath, uid string, permission enum.Permission) error {
|
||||||
|
scope := &types.Scope{SpacePath: parentPath}
|
||||||
|
resource := &types.Resource{
|
||||||
|
Type: enum.ResourceTypePipeline,
|
||||||
|
Name: uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Check(ctx, authorizer, session, scope, resource, permission)
|
||||||
|
}
|
29
internal/api/auth/secret.go
Normal file
29
internal/api/auth/secret.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckSecret checks if a repo specific permission is granted for the current auth session
|
||||||
|
// in the scope of its parent.
|
||||||
|
// Returns nil if the permission is granted, otherwise returns an error.
|
||||||
|
// NotAuthenticated, NotAuthorized, or any underlying error.
|
||||||
|
func CheckSecret(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||||
|
parentPath, uid string, permission enum.Permission) error {
|
||||||
|
scope := &types.Scope{SpacePath: parentPath}
|
||||||
|
resource := &types.Resource{
|
||||||
|
Type: enum.ResourceTypeSecret,
|
||||||
|
Name: uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Check(ctx, authorizer, session, scope, resource, permission)
|
||||||
|
}
|
@ -51,27 +51,29 @@ func (in *ReportInput) Validate() error {
|
|||||||
}
|
}
|
||||||
in.Payload.Kind = payloadKind
|
in.Payload.Kind = payloadKind
|
||||||
|
|
||||||
//nolint:gocritic // more values to follow on the enum (we want linter warning in case it is missed)
|
|
||||||
switch in.Payload.Kind {
|
switch in.Payload.Kind {
|
||||||
case enum.CheckPayloadKindExternal:
|
case enum.CheckPayloadKindEmpty:
|
||||||
// the default external type does not support payload: clear it here
|
// the default payload kind (empty) does not support the payload data: clear it here
|
||||||
|
in.Payload.Version = ""
|
||||||
|
in.Payload.Data = []byte("{}")
|
||||||
|
|
||||||
var err error
|
if in.Link == "" { // the link is mandatory as there is nothing in the payload
|
||||||
|
|
||||||
if in.Link == "" { // the link is mandatory for the external
|
|
||||||
return usererror.BadRequest("Link is missing")
|
return usererror.BadRequest("Link is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case enum.CheckPayloadKindRaw, enum.CheckPayloadKindMarkdown:
|
||||||
|
// the text payload kinds (raw and markdown) do not support the version
|
||||||
if in.Payload.Version != "" {
|
if in.Payload.Version != "" {
|
||||||
return usererror.BadRequest("Payload version must be empty")
|
return usererror.BadRequestf("Payload version must be empty for the payload kind '%s'",
|
||||||
|
in.Payload.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
in.Payload.Data, err = sanitizeJsonPayload(in.Payload.Data, &struct {
|
payloadDataJSON, err := sanitizeJsonPayload(in.Payload.Data, &types.CheckPayloadText{})
|
||||||
Details string `json:"details"`
|
|
||||||
}{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
in.Payload.Data = payloadDataJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
36
internal/api/controller/execution/controller.go
Normal file
36
internal/api/controller/execution/controller.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
authorizer authz.Authorizer
|
||||||
|
executionStore store.ExecutionStore
|
||||||
|
pipelineStore store.PipelineStore
|
||||||
|
spaceStore store.SpaceStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(
|
||||||
|
db *sqlx.DB,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
executionStore store.ExecutionStore,
|
||||||
|
pipelineStore store.PipelineStore,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return &Controller{
|
||||||
|
db: db,
|
||||||
|
authorizer: authorizer,
|
||||||
|
executionStore: executionStore,
|
||||||
|
pipelineStore: pipelineStore,
|
||||||
|
spaceStore: spaceStore,
|
||||||
|
}
|
||||||
|
}
|
66
internal/api/controller/execution/create.go
Normal file
66
internal/api/controller/execution/create.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Add more as needed.
|
||||||
|
type CreateInput struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
uid string,
|
||||||
|
in *CreateInput,
|
||||||
|
) (*types.Execution, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineExecute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err = c.pipelineStore.IncrementSeqNum(ctx, pipeline)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to increment sequence number: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
execution := &types.Execution{
|
||||||
|
Number: pipeline.Seq,
|
||||||
|
Status: in.Status,
|
||||||
|
RepoID: pipeline.RepoID,
|
||||||
|
PipelineID: pipeline.ID,
|
||||||
|
Created: now,
|
||||||
|
Updated: now,
|
||||||
|
Version: 0,
|
||||||
|
}
|
||||||
|
err = c.executionStore.Create(ctx, execution)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("execution creation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return execution, nil
|
||||||
|
}
|
41
internal/api/controller/execution/delete.go
Normal file
41
internal/api/controller/execution/delete.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Delete(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
pipelineUID string,
|
||||||
|
executionNum int64,
|
||||||
|
) error {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineDelete)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
err = c.executionStore.Delete(ctx, pipeline.ID, executionNum)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not delete execution: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
40
internal/api/controller/execution/find.go
Normal file
40
internal/api/controller/execution/find.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Find(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
pipelineUID string,
|
||||||
|
executionNum int64,
|
||||||
|
) (*types.Execution, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineView)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.executionStore.Find(ctx, pipeline.ID, executionNum)
|
||||||
|
}
|
59
internal/api/controller/execution/list.go
Normal file
59
internal/api/controller/execution/list.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) List(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
pipelineUID string,
|
||||||
|
pagination types.Pagination,
|
||||||
|
) ([]*types.Execution, int64, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
|
||||||
|
}
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineView)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
var executions []*types.Execution
|
||||||
|
|
||||||
|
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
|
||||||
|
count, err = c.executionStore.Count(ctx, pipeline.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to count child executions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
executions, err = c.executionStore.List(ctx, pipeline.ID, pagination)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list child executions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}, dbtx.TxDefaultReadOnly)
|
||||||
|
if err != nil {
|
||||||
|
return executions, count, fmt.Errorf("failed to fetch list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return executions, count, nil
|
||||||
|
}
|
57
internal/api/controller/execution/update.go
Normal file
57
internal/api/controller/execution/update.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateInput struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
pipelineUID string,
|
||||||
|
executionNum int64,
|
||||||
|
in *UpdateInput) (*types.Execution, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipelineUID, enum.PermissionPipelineEdit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to check auth: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
execution, err := c.executionStore.Find(ctx, pipeline.ID, executionNum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find execution: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.executionStore.UpdateOptLock(ctx,
|
||||||
|
execution, func(original *types.Execution) error {
|
||||||
|
// update values only if provided
|
||||||
|
if in.Status != "" {
|
||||||
|
original.Status = in.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
27
internal/api/controller/execution/wire.go
Normal file
27
internal/api/controller/execution/wire.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WireSet provides a wire set for this package.
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvideController,
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvideController(db *sqlx.DB,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
executionStore store.ExecutionStore,
|
||||||
|
pipelineStore store.PipelineStore,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return NewController(db, authorizer, executionStore, pipelineStore, spaceStore)
|
||||||
|
}
|
44
internal/api/controller/pipeline/controller.go
Normal file
44
internal/api/controller/pipeline/controller.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
defaultBranch string
|
||||||
|
db *sqlx.DB
|
||||||
|
uidCheck check.PathUID
|
||||||
|
pathStore store.PathStore
|
||||||
|
repoStore store.RepoStore
|
||||||
|
authorizer authz.Authorizer
|
||||||
|
pipelineStore store.PipelineStore
|
||||||
|
spaceStore store.SpaceStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(
|
||||||
|
db *sqlx.DB,
|
||||||
|
uidCheck check.PathUID,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
pathStore store.PathStore,
|
||||||
|
repoStore store.RepoStore,
|
||||||
|
pipelineStore store.PipelineStore,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return &Controller{
|
||||||
|
db: db,
|
||||||
|
uidCheck: uidCheck,
|
||||||
|
pathStore: pathStore,
|
||||||
|
repoStore: repoStore,
|
||||||
|
authorizer: authorizer,
|
||||||
|
pipelineStore: pipelineStore,
|
||||||
|
spaceStore: spaceStore,
|
||||||
|
}
|
||||||
|
}
|
106
internal/api/controller/pipeline/create.go
Normal file
106
internal/api/controller/pipeline/create.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errPipelineRequiresParent if the user tries to create a pipeline without a parent space.
|
||||||
|
errPipelineRequiresParent = usererror.BadRequest(
|
||||||
|
"Parent space required - standalone pipelines are not supported.")
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateInput struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
SpaceRef string `json:"space_ref"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
RepoRef string `json:"repo_ref"` // empty if repo_type != gitness
|
||||||
|
RepoType enum.ScmType `json:"repo_type"`
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
ConfigPath string `json:"config_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Pipeline, error) {
|
||||||
|
parentSpace, err := c.spaceStore.FindByRef(ctx, in.SpaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find parent by ref: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, parentSpace.Path, in.UID, enum.PermissionPipelineEdit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var repoID int64
|
||||||
|
|
||||||
|
if in.RepoType == enum.ScmTypeGitness {
|
||||||
|
repo, err := c.repoStore.FindByRef(ctx, in.RepoRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find repo by ref: %w", err)
|
||||||
|
}
|
||||||
|
repoID = repo.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.sanitizeCreateInput(in); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to sanitize input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipeline *types.Pipeline
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
pipeline = &types.Pipeline{
|
||||||
|
Description: in.Description,
|
||||||
|
SpaceID: parentSpace.ID,
|
||||||
|
UID: in.UID,
|
||||||
|
Seq: 0,
|
||||||
|
RepoID: repoID,
|
||||||
|
RepoType: in.RepoType,
|
||||||
|
DefaultBranch: in.DefaultBranch,
|
||||||
|
ConfigPath: in.ConfigPath,
|
||||||
|
Created: now,
|
||||||
|
Updated: now,
|
||||||
|
Version: 0,
|
||||||
|
}
|
||||||
|
err = c.pipelineStore.Create(ctx, pipeline)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("pipeline creation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipeline, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
|
||||||
|
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
|
||||||
|
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
|
||||||
|
return errPipelineRequiresParent
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.uidCheck(in.UID, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
in.Description = strings.TrimSpace(in.Description)
|
||||||
|
if err := check.Description(in.Description); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.DefaultBranch == "" {
|
||||||
|
in.DefaultBranch = c.defaultBranch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
31
internal/api/controller/pipeline/delete.go
Normal file
31
internal/api/controller/pipeline/delete.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Delete(ctx context.Context, session *auth.Session, spaceRef string, uid string) error {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineDelete)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
err = c.pipelineStore.DeleteByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not delete pipeline: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
32
internal/api/controller/pipeline/find.go
Normal file
32
internal/api/controller/pipeline/find.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Find(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
uid string,
|
||||||
|
) (*types.Pipeline, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find parent space: %w", err)
|
||||||
|
}
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineView)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
return c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||||
|
}
|
58
internal/api/controller/pipeline/update.go
Normal file
58
internal/api/controller/pipeline/update.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateInput struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
ConfigPath string `json:"config_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
uid string,
|
||||||
|
in *UpdateInput,
|
||||||
|
) (*types.Pipeline, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineEdit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find pipeline: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.pipelineStore.UpdateOptLock(ctx, pipeline, func(pipeline *types.Pipeline) error {
|
||||||
|
if in.Description != "" {
|
||||||
|
pipeline.Description = in.Description
|
||||||
|
}
|
||||||
|
if in.UID != "" {
|
||||||
|
pipeline.UID = in.UID
|
||||||
|
}
|
||||||
|
if in.ConfigPath != "" {
|
||||||
|
pipeline.ConfigPath = in.ConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
30
internal/api/controller/pipeline/wire.go
Normal file
30
internal/api/controller/pipeline/wire.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WireSet provides a wire set for this package.
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvideController,
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvideController(db *sqlx.DB,
|
||||||
|
uidCheck check.PathUID,
|
||||||
|
pathStore store.PathStore,
|
||||||
|
repoStore store.RepoStore,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
pipelineStore store.PipelineStore,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return NewController(db, uidCheck, authorizer, pathStore, repoStore, pipelineStore, spaceStore)
|
||||||
|
}
|
@ -73,12 +73,11 @@ func (c *Controller) CommentStatus(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UnixMilli()
|
|
||||||
act.Edited = now
|
|
||||||
|
|
||||||
act.Resolved = nil
|
act.Resolved = nil
|
||||||
act.ResolvedBy = nil
|
act.ResolvedBy = nil
|
||||||
|
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
if in.Status != enum.PullReqCommentStatusActive {
|
if in.Status != enum.PullReqCommentStatusActive {
|
||||||
// In the future if we add more comment resolved statuses
|
// In the future if we add more comment resolved statuses
|
||||||
// we'll add the ResolvedReason field and put the reason there.
|
// we'll add the ResolvedReason field and put the reason there.
|
||||||
|
@ -80,13 +80,15 @@ type SubmoduleContent struct {
|
|||||||
|
|
||||||
func (c *SubmoduleContent) isContent() {}
|
func (c *SubmoduleContent) isContent() {}
|
||||||
|
|
||||||
/*
|
// GetContent finds the content of the repo at the given path.
|
||||||
* GetContent finds the content of the repo at the given path.
|
// If no gitRef is provided, the content is retrieved from the default branch.
|
||||||
* If no gitRef is provided, the content is retrieved from the default branch.
|
func (c *Controller) GetContent(ctx context.Context,
|
||||||
* If includeLatestCommit is enabled, the response contains information of the latest commit that changed the object.
|
session *auth.Session,
|
||||||
*/
|
repoRef string,
|
||||||
func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repoRef string,
|
gitRef string,
|
||||||
gitRef string, repoPath string, includeLatestCommit bool) (*GetContentOutput, error) {
|
repoPath string,
|
||||||
|
includeLatestCommit bool,
|
||||||
|
) (*GetContentOutput, error) {
|
||||||
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -114,7 +116,7 @@ func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := mapToContentInfo(&treeNodeOutput.Node, treeNodeOutput.Commit)
|
info, err := mapToContentInfo(treeNodeOutput.Node, treeNodeOutput.Commit, includeLatestCommit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -122,8 +124,7 @@ func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repo
|
|||||||
var content Content
|
var content Content
|
||||||
switch info.Type {
|
switch info.Type {
|
||||||
case ContentTypeDir:
|
case ContentTypeDir:
|
||||||
// for getContent we don't want any recursiveness for dir content.
|
content, err = c.getDirContent(ctx, readParams, gitRef, repoPath, includeLatestCommit)
|
||||||
content, err = c.getDirContent(ctx, readParams, gitRef, repoPath, includeLatestCommit, false)
|
|
||||||
case ContentTypeFile:
|
case ContentTypeFile:
|
||||||
content, err = c.getFileContent(ctx, readParams, info.SHA)
|
content, err = c.getFileContent(ctx, readParams, info.SHA)
|
||||||
case ContentTypeSymlink:
|
case ContentTypeSymlink:
|
||||||
@ -139,13 +140,17 @@ func (c *Controller) GetContent(ctx context.Context, session *auth.Session, repo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &GetContentOutput{
|
return &GetContentOutput{
|
||||||
ContentInfo: *info,
|
ContentInfo: info,
|
||||||
Content: content,
|
Content: content,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getSubmoduleContent(ctx context.Context, readParams gitrpc.ReadParams, gitRef string,
|
func (c *Controller) getSubmoduleContent(ctx context.Context,
|
||||||
repoPath string, commitSHA string) (*SubmoduleContent, error) {
|
readParams gitrpc.ReadParams,
|
||||||
|
gitRef string,
|
||||||
|
repoPath string,
|
||||||
|
commitSHA string,
|
||||||
|
) (*SubmoduleContent, error) {
|
||||||
output, err := c.gitRPCClient.GetSubmodule(ctx, &gitrpc.GetSubmoduleParams{
|
output, err := c.gitRPCClient.GetSubmodule(ctx, &gitrpc.GetSubmoduleParams{
|
||||||
ReadParams: readParams,
|
ReadParams: readParams,
|
||||||
GitREF: gitRef,
|
GitREF: gitRef,
|
||||||
@ -163,8 +168,10 @@ func (c *Controller) getSubmoduleContent(ctx context.Context, readParams gitrpc.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getFileContent(ctx context.Context, readParams gitrpc.ReadParams,
|
func (c *Controller) getFileContent(ctx context.Context,
|
||||||
blobSHA string) (*FileContent, error) {
|
readParams gitrpc.ReadParams,
|
||||||
|
blobSHA string,
|
||||||
|
) (*FileContent, error) {
|
||||||
output, err := c.gitRPCClient.GetBlob(ctx, &gitrpc.GetBlobParams{
|
output, err := c.gitRPCClient.GetBlob(ctx, &gitrpc.GetBlobParams{
|
||||||
ReadParams: readParams,
|
ReadParams: readParams,
|
||||||
SHA: blobSHA,
|
SHA: blobSHA,
|
||||||
@ -187,8 +194,10 @@ func (c *Controller) getFileContent(ctx context.Context, readParams gitrpc.ReadP
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getSymlinkContent(ctx context.Context, readParams gitrpc.ReadParams,
|
func (c *Controller) getSymlinkContent(ctx context.Context,
|
||||||
blobSHA string) (*SymlinkContent, error) {
|
readParams gitrpc.ReadParams,
|
||||||
|
blobSHA string,
|
||||||
|
) (*SymlinkContent, error) {
|
||||||
output, err := c.gitRPCClient.GetBlob(ctx, &gitrpc.GetBlobParams{
|
output, err := c.gitRPCClient.GetBlob(ctx, &gitrpc.GetBlobParams{
|
||||||
ReadParams: readParams,
|
ReadParams: readParams,
|
||||||
SHA: blobSHA,
|
SHA: blobSHA,
|
||||||
@ -211,14 +220,17 @@ func (c *Controller) getSymlinkContent(ctx context.Context, readParams gitrpc.Re
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getDirContent(ctx context.Context, readParams gitrpc.ReadParams, gitRef string,
|
func (c *Controller) getDirContent(ctx context.Context,
|
||||||
repoPath string, includeLatestCommit bool, recursive bool) (*DirContent, error) {
|
readParams gitrpc.ReadParams,
|
||||||
|
gitRef string,
|
||||||
|
repoPath string,
|
||||||
|
includeLatestCommit bool,
|
||||||
|
) (*DirContent, error) {
|
||||||
output, err := c.gitRPCClient.ListTreeNodes(ctx, &gitrpc.ListTreeNodeParams{
|
output, err := c.gitRPCClient.ListTreeNodes(ctx, &gitrpc.ListTreeNodeParams{
|
||||||
ReadParams: readParams,
|
ReadParams: readParams,
|
||||||
GitREF: gitRef,
|
GitREF: gitRef,
|
||||||
Path: repoPath,
|
Path: repoPath,
|
||||||
IncludeLatestCommit: includeLatestCommit,
|
IncludeLatestCommit: includeLatestCommit,
|
||||||
Recursive: recursive,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: handle not found error
|
// TODO: handle not found error
|
||||||
@ -227,15 +239,11 @@ func (c *Controller) getDirContent(ctx context.Context, readParams gitrpc.ReadPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
entries := make([]ContentInfo, len(output.Nodes))
|
entries := make([]ContentInfo, len(output.Nodes))
|
||||||
for i := range output.Nodes {
|
for i, node := range output.Nodes {
|
||||||
node := output.Nodes[i]
|
entries[i], err = mapToContentInfo(node, nil, false)
|
||||||
|
|
||||||
var entry *ContentInfo
|
|
||||||
entry, err = mapToContentInfo(&node.TreeNode, node.Commit)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
entries[i] = *entry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DirContent{
|
return &DirContent{
|
||||||
@ -243,17 +251,13 @@ func (c *Controller) getDirContent(ctx context.Context, readParams gitrpc.ReadPa
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapToContentInfo(node *gitrpc.TreeNode, commit *gitrpc.Commit) (*ContentInfo, error) {
|
func mapToContentInfo(node gitrpc.TreeNode, commit *gitrpc.Commit, includeLatestCommit bool) (ContentInfo, error) {
|
||||||
// node data is expected
|
|
||||||
if node == nil {
|
|
||||||
return nil, fmt.Errorf("node can't be nil")
|
|
||||||
}
|
|
||||||
typ, err := mapNodeModeToContentType(node.Mode)
|
typ, err := mapNodeModeToContentType(node.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ContentInfo{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &ContentInfo{
|
res := ContentInfo{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
SHA: node.SHA,
|
SHA: node.SHA,
|
||||||
Name: node.Name,
|
Name: node.Name,
|
||||||
@ -261,10 +265,10 @@ func mapToContentInfo(node *gitrpc.TreeNode, commit *gitrpc.Commit) (*ContentInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse commit only if available
|
// parse commit only if available
|
||||||
if commit != nil {
|
if commit != nil && includeLatestCommit {
|
||||||
res.LatestCommit, err = controller.MapCommit(commit)
|
res.LatestCommit, err = controller.MapCommit(commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ContentInfo{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
70
internal/api/controller/repo/content_paths_details.go
Normal file
70
internal/api/controller/repo/content_paths_details.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/gitrpc"
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PathsDetailsInput struct {
|
||||||
|
Paths []string `json:"paths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathsDetailsOutput struct {
|
||||||
|
Details []gitrpc.PathDetails `json:"details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathsDetails finds the additional info about the provided paths of the repo.
|
||||||
|
// If no gitRef is provided, the content is retrieved from the default branch.
|
||||||
|
func (c *Controller) PathsDetails(ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
repoRef string,
|
||||||
|
gitRef string,
|
||||||
|
input PathsDetailsInput,
|
||||||
|
) (PathsDetailsOutput, error) {
|
||||||
|
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
||||||
|
if err != nil {
|
||||||
|
return PathsDetailsOutput{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoView, true); err != nil {
|
||||||
|
return PathsDetailsOutput{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(input.Paths) == 0 {
|
||||||
|
return PathsDetailsOutput{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(input.Paths) > 50 {
|
||||||
|
return PathsDetailsOutput{}, usererror.BadRequest("maximum number of elements in the Paths array is 25")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set gitRef to default branch in case an empty reference was provided
|
||||||
|
if gitRef == "" {
|
||||||
|
gitRef = repo.DefaultBranch
|
||||||
|
}
|
||||||
|
|
||||||
|
// create read params once
|
||||||
|
readParams := CreateRPCReadParams(repo)
|
||||||
|
|
||||||
|
result, err := c.gitRPCClient.PathsDetails(ctx, gitrpc.PathsDetailsParams{
|
||||||
|
ReadParams: readParams,
|
||||||
|
GitREF: gitRef,
|
||||||
|
Paths: input.Paths,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return PathsDetailsOutput{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return PathsDetailsOutput{
|
||||||
|
Details: result.Details,
|
||||||
|
}, nil
|
||||||
|
}
|
@ -10,9 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/harness/gitness/gitrpc"
|
"github.com/harness/gitness/gitrpc"
|
||||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
"github.com/harness/gitness/internal/api/usererror"
|
|
||||||
"github.com/harness/gitness/internal/auth"
|
"github.com/harness/gitness/internal/auth"
|
||||||
"github.com/harness/gitness/types/check"
|
|
||||||
"github.com/harness/gitness/types/enum"
|
"github.com/harness/gitness/types/enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,11 +40,6 @@ func (c *Controller) CreateBranch(ctx context.Context, session *auth.Session,
|
|||||||
in.Target = repo.DefaultBranch
|
in.Target = repo.DefaultBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkBranchName(in.Name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("branch name failed check: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
writeParams, err := CreateRPCWriteParams(ctx, c.urlProvider, session, repo)
|
writeParams, err := CreateRPCWriteParams(ctx, c.urlProvider, session, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
return nil, fmt.Errorf("failed to create RPC write params: %w", err)
|
||||||
@ -68,15 +61,3 @@ func (c *Controller) CreateBranch(ctx context.Context, session *auth.Session,
|
|||||||
|
|
||||||
return &branch, nil
|
return &branch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkBranchName does some basic branch validation
|
|
||||||
// We only ensure there are no control characters, the rest is up to git.
|
|
||||||
// TODO: Do we need some more validation here?
|
|
||||||
func checkBranchName(name string) error {
|
|
||||||
// fail fast on missing name
|
|
||||||
if len(name) == 0 {
|
|
||||||
return usererror.ErrBadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
return check.ForControlCharacters(name)
|
|
||||||
}
|
|
||||||
|
@ -20,14 +20,14 @@ import (
|
|||||||
* ListCommits lists the commits of a repo.
|
* ListCommits lists the commits of a repo.
|
||||||
*/
|
*/
|
||||||
func (c *Controller) ListCommits(ctx context.Context, session *auth.Session,
|
func (c *Controller) ListCommits(ctx context.Context, session *auth.Session,
|
||||||
repoRef string, gitRef string, filter *types.CommitFilter) ([]types.Commit, []types.RenameDetails, error) {
|
repoRef string, gitRef string, filter *types.CommitFilter) (types.ListCommitResponse, error) {
|
||||||
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return types.ListCommitResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoView, false); err != nil {
|
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoView, false); err != nil {
|
||||||
return nil, nil, err
|
return types.ListCommitResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// set gitRef to default branch in case an empty reference was provided
|
// set gitRef to default branch in case an empty reference was provided
|
||||||
@ -47,7 +47,7 @@ func (c *Controller) ListCommits(ctx context.Context, session *auth.Session,
|
|||||||
Committer: filter.Committer,
|
Committer: filter.Committer,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return types.ListCommitResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
commits := make([]types.Commit, len(rpcOut.Commits))
|
commits := make([]types.Commit, len(rpcOut.Commits))
|
||||||
@ -55,7 +55,7 @@ func (c *Controller) ListCommits(ctx context.Context, session *auth.Session,
|
|||||||
var commit *types.Commit
|
var commit *types.Commit
|
||||||
commit, err = controller.MapCommit(&rpcOut.Commits[i])
|
commit, err = controller.MapCommit(&rpcOut.Commits[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to map commit: %w", err)
|
return types.ListCommitResponse{}, fmt.Errorf("failed to map commit: %w", err)
|
||||||
}
|
}
|
||||||
commits[i] = *commit
|
commits[i] = *commit
|
||||||
}
|
}
|
||||||
@ -64,9 +64,13 @@ func (c *Controller) ListCommits(ctx context.Context, session *auth.Session,
|
|||||||
for i := range rpcOut.RenameDetails {
|
for i := range rpcOut.RenameDetails {
|
||||||
renameDetails := controller.MapRenameDetails(rpcOut.RenameDetails[i])
|
renameDetails := controller.MapRenameDetails(rpcOut.RenameDetails[i])
|
||||||
if renameDetails == nil {
|
if renameDetails == nil {
|
||||||
return nil, nil, fmt.Errorf("rename details was nil")
|
return types.ListCommitResponse{}, fmt.Errorf("rename details was nil")
|
||||||
}
|
}
|
||||||
renameDetailList[i] = *renameDetails
|
renameDetailList[i] = *renameDetails
|
||||||
}
|
}
|
||||||
return commits, renameDetailList, nil
|
return types.ListCommitResponse{
|
||||||
|
Commits: commits,
|
||||||
|
RenameDetails: renameDetailList,
|
||||||
|
TotalCommits: rpcOut.TotalCommits,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
44
internal/api/controller/secret/controller.go
Normal file
44
internal/api/controller/secret/controller.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/encrypt"
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
uidCheck check.PathUID
|
||||||
|
pathStore store.PathStore
|
||||||
|
encrypter encrypt.Encrypter
|
||||||
|
secretStore store.SecretStore
|
||||||
|
authorizer authz.Authorizer
|
||||||
|
spaceStore store.SpaceStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(
|
||||||
|
db *sqlx.DB,
|
||||||
|
uidCheck check.PathUID,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
pathStore store.PathStore,
|
||||||
|
encrypter encrypt.Encrypter,
|
||||||
|
secretStore store.SecretStore,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return &Controller{
|
||||||
|
db: db,
|
||||||
|
uidCheck: uidCheck,
|
||||||
|
pathStore: pathStore,
|
||||||
|
encrypter: encrypter,
|
||||||
|
secretStore: secretStore,
|
||||||
|
authorizer: authorizer,
|
||||||
|
spaceStore: spaceStore,
|
||||||
|
}
|
||||||
|
}
|
122
internal/api/controller/secret/create.go
Normal file
122
internal/api/controller/secret/create.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/encrypt"
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errSecretRequiresParent if the user tries to create a secret without a parent space.
|
||||||
|
errSecretRequiresParent = usererror.BadRequest(
|
||||||
|
"Parent space required - standalone secret are not supported.")
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateInput struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
SpaceRef string `json:"space_ref"` // Ref of the parent space
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Secret, error) {
|
||||||
|
parentSpace, err := c.spaceStore.FindByRef(ctx, in.SpaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find parent by ref: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckSecret(ctx, c.authorizer, session, parentSpace.Path, in.UID, enum.PermissionSecretEdit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.sanitizeCreateInput(in); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to sanitize input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var secret *types.Secret
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
secret = &types.Secret{
|
||||||
|
Description: in.Description,
|
||||||
|
Data: in.Data,
|
||||||
|
SpaceID: parentSpace.ID,
|
||||||
|
UID: in.UID,
|
||||||
|
Created: now,
|
||||||
|
Updated: now,
|
||||||
|
Version: 0,
|
||||||
|
}
|
||||||
|
secret, err = enc(c.encrypter, secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not encrypt secret: %w", err)
|
||||||
|
}
|
||||||
|
err = c.secretStore.Create(ctx, secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("secret creation failed: %w", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
|
||||||
|
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
|
||||||
|
|
||||||
|
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
|
||||||
|
return errSecretRequiresParent
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.uidCheck(in.UID, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
in.Description = strings.TrimSpace(in.Description)
|
||||||
|
if err := check.Description(in.Description); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function returns the same secret with encrypted data.
|
||||||
|
func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
|
||||||
|
if secret == nil {
|
||||||
|
return nil, fmt.Errorf("cannot encrypt a nil secret")
|
||||||
|
}
|
||||||
|
s := *secret
|
||||||
|
ciphertext, err := encrypt.Encrypt(secret.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.Data = string(ciphertext)
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function returns the same secret with decrypted data.
|
||||||
|
func dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
|
||||||
|
if secret == nil {
|
||||||
|
return nil, fmt.Errorf("cannot decrypt a nil secret")
|
||||||
|
}
|
||||||
|
s := *secret
|
||||||
|
plaintext, err := encrypt.Decrypt([]byte(secret.Data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.Data = plaintext
|
||||||
|
return &s, nil
|
||||||
|
}
|
31
internal/api/controller/secret/delete.go
Normal file
31
internal/api/controller/secret/delete.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Delete(ctx context.Context, session *auth.Session, spaceRef string, uid string) error {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretDelete)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to authorize: %w", err)
|
||||||
|
}
|
||||||
|
err = c.secretStore.DeleteByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not delete secret: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
40
internal/api/controller/secret/find.go
Normal file
40
internal/api/controller/secret/find.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) Find(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
uid string,
|
||||||
|
) (*types.Secret, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find space: %w", err)
|
||||||
|
}
|
||||||
|
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretView)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to authorize: %w", err)
|
||||||
|
}
|
||||||
|
secret, err := c.secretStore.FindByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find secret: %w", err)
|
||||||
|
}
|
||||||
|
secret, err = dec(c.encrypter, secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not decrypt secret: %w", err)
|
||||||
|
}
|
||||||
|
return secret, nil
|
||||||
|
}
|
63
internal/api/controller/secret/update.go
Normal file
63
internal/api/controller/secret/update.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateInput is used for updating a repo.
|
||||||
|
type UpdateInput struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
uid string,
|
||||||
|
in *UpdateInput,
|
||||||
|
) (*types.Secret, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretEdit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := c.secretStore.FindByUID(ctx, space.ID, uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find secret: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.secretStore.UpdateOptLock(ctx, secret, func(original *types.Secret) error {
|
||||||
|
if in.Description != "" {
|
||||||
|
original.Description = in.Description
|
||||||
|
}
|
||||||
|
if in.Data != "" {
|
||||||
|
data, err := c.encrypter.Encrypt(original.Data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not encrypt secret: %w", err)
|
||||||
|
}
|
||||||
|
original.Data = string(data)
|
||||||
|
}
|
||||||
|
if in.UID != "" {
|
||||||
|
original.UID = in.UID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
31
internal/api/controller/secret/wire.go
Normal file
31
internal/api/controller/secret/wire.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/harness/gitness/encrypt"
|
||||||
|
"github.com/harness/gitness/internal/auth/authz"
|
||||||
|
"github.com/harness/gitness/internal/store"
|
||||||
|
"github.com/harness/gitness/types/check"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WireSet provides a wire set for this package.
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvideController,
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvideController(db *sqlx.DB,
|
||||||
|
uidCheck check.PathUID,
|
||||||
|
pathStore store.PathStore,
|
||||||
|
encrypter encrypt.Encrypter,
|
||||||
|
secretStore store.SecretStore,
|
||||||
|
authorizer authz.Authorizer,
|
||||||
|
spaceStore store.SpaceStore,
|
||||||
|
) *Controller {
|
||||||
|
return NewController(db, uidCheck, authorizer, pathStore, encrypter, secretStore, spaceStore)
|
||||||
|
}
|
@ -20,6 +20,8 @@ type Controller struct {
|
|||||||
uidCheck check.PathUID
|
uidCheck check.PathUID
|
||||||
authorizer authz.Authorizer
|
authorizer authz.Authorizer
|
||||||
pathStore store.PathStore
|
pathStore store.PathStore
|
||||||
|
pipelineStore store.PipelineStore
|
||||||
|
secretStore store.SecretStore
|
||||||
spaceStore store.SpaceStore
|
spaceStore store.SpaceStore
|
||||||
repoStore store.RepoStore
|
repoStore store.RepoStore
|
||||||
principalStore store.PrincipalStore
|
principalStore store.PrincipalStore
|
||||||
@ -29,9 +31,9 @@ type Controller struct {
|
|||||||
|
|
||||||
func NewController(db *sqlx.DB, urlProvider *url.Provider,
|
func NewController(db *sqlx.DB, urlProvider *url.Provider,
|
||||||
uidCheck check.PathUID, authorizer authz.Authorizer,
|
uidCheck check.PathUID, authorizer authz.Authorizer,
|
||||||
pathStore store.PathStore, spaceStore store.SpaceStore,
|
pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore,
|
||||||
repoStore store.RepoStore, principalStore store.PrincipalStore, repoCtrl *repo.Controller,
|
spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore,
|
||||||
membershipStore store.MembershipStore,
|
repoCtrl *repo.Controller, membershipStore store.MembershipStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
db: db,
|
db: db,
|
||||||
@ -39,6 +41,8 @@ func NewController(db *sqlx.DB, urlProvider *url.Provider,
|
|||||||
uidCheck: uidCheck,
|
uidCheck: uidCheck,
|
||||||
authorizer: authorizer,
|
authorizer: authorizer,
|
||||||
pathStore: pathStore,
|
pathStore: pathStore,
|
||||||
|
pipelineStore: pipelineStore,
|
||||||
|
secretStore: secretStore,
|
||||||
spaceStore: spaceStore,
|
spaceStore: spaceStore,
|
||||||
repoStore: repoStore,
|
repoStore: repoStore,
|
||||||
principalStore: principalStore,
|
principalStore: principalStore,
|
||||||
|
54
internal/api/controller/space/list_pipelines.go
Normal file
54
internal/api/controller/space/list_pipelines.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListPipelines lists the pipelines in a space.
|
||||||
|
func (c *Controller) ListPipelines(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
filter types.ListQueryFilter,
|
||||||
|
) ([]*types.Pipeline, int64, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionPipelineView, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
var pipelines []*types.Pipeline
|
||||||
|
|
||||||
|
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
|
||||||
|
count, err = c.pipelineStore.Count(ctx, space.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to count child executions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipelines, err = c.pipelineStore.List(ctx, space.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to count child executions: %w", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}, dbtx.TxDefaultReadOnly)
|
||||||
|
if err != nil {
|
||||||
|
return pipelines, count, fmt.Errorf("failed to list pipelines: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelines, count, nil
|
||||||
|
}
|
54
internal/api/controller/space/list_secrets.go
Normal file
54
internal/api/controller/space/list_secrets.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||||
|
"github.com/harness/gitness/internal/auth"
|
||||||
|
"github.com/harness/gitness/store/database/dbtx"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
"github.com/harness/gitness/types/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListSecrets lists the secrets in a space.
|
||||||
|
func (c *Controller) ListSecrets(
|
||||||
|
ctx context.Context,
|
||||||
|
session *auth.Session,
|
||||||
|
spaceRef string,
|
||||||
|
filter types.ListQueryFilter,
|
||||||
|
) ([]*types.Secret, int64, error) {
|
||||||
|
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSecretView, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("could not authorize: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
var secrets []*types.Secret
|
||||||
|
|
||||||
|
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
|
||||||
|
count, err = c.secretStore.Count(ctx, space.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to count child executions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err = c.secretStore.List(ctx, space.ID, filter)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list child executions: %w", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}, dbtx.TxDefaultReadOnly)
|
||||||
|
if err != nil {
|
||||||
|
return secrets, count, fmt.Errorf("failed to list secrets: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return secrets, count, nil
|
||||||
|
}
|
@ -21,12 +21,11 @@ var WireSet = wire.NewSet(
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ProvideController(db *sqlx.DB, urlProvider *url.Provider, uidCheck check.PathUID, authorizer authz.Authorizer,
|
func ProvideController(db *sqlx.DB, urlProvider *url.Provider, uidCheck check.PathUID, authorizer authz.Authorizer,
|
||||||
pathStore store.PathStore, spaceStore store.SpaceStore, repoStore store.RepoStore,
|
pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore,
|
||||||
principalStore store.PrincipalStore, repoCtrl *repo.Controller,
|
spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore,
|
||||||
membershipStore store.MembershipStore,
|
repoCtrl *repo.Controller, membershipStore store.MembershipStore,
|
||||||
) *Controller {
|
) *Controller {
|
||||||
return NewController(db, urlProvider, uidCheck, authorizer,
|
return NewController(db, urlProvider, uidCheck, authorizer,
|
||||||
pathStore, spaceStore, repoStore,
|
pathStore, pipelineStore, secretStore, spaceStore, repoStore,
|
||||||
principalStore, repoCtrl,
|
principalStore, repoCtrl, membershipStore)
|
||||||
membershipStore)
|
|
||||||
}
|
}
|
||||||
|
47
internal/api/handler/execution/create.go
Normal file
47
internal/api/handler/execution/create.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleCreate(executionCtrl *execution.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := new(execution.CreateInput)
|
||||||
|
err = json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
execution, err := executionCtrl.Create(ctx, session, spaceRef, pipelineUID, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusCreated, execution)
|
||||||
|
}
|
||||||
|
}
|
44
internal/api/handler/execution/delete.go
Normal file
44
internal/api/handler/execution/delete.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleDelete(executionCtrl *execution.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := request.GetExecutionNumberFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = executionCtrl.Delete(ctx, session, spaceRef, pipelineUID, n)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.DeleteSuccessful(w)
|
||||||
|
}
|
||||||
|
}
|
44
internal/api/handler/execution/find.go
Normal file
44
internal/api/handler/execution/find.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleFind(executionCtrl *execution.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := request.GetExecutionNumberFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
execution, err := executionCtrl.Find(ctx, session, spaceRef, pipelineUID, n)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, execution)
|
||||||
|
}
|
||||||
|
}
|
42
internal/api/handler/execution/list.go
Normal file
42
internal/api/handler/execution/list.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleList(executionCtrl *execution.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination := request.ParsePaginationFromRequest(r)
|
||||||
|
|
||||||
|
repos, totalCount, err := executionCtrl.List(ctx, session, spaceRef, pipelineUID, pagination)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Pagination(r, w, pagination.Page, pagination.Size, int(totalCount))
|
||||||
|
render.JSON(w, http.StatusOK, repos)
|
||||||
|
}
|
||||||
|
}
|
53
internal/api/handler/execution/update.go
Normal file
53
internal/api/handler/execution/update.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package execution
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleUpdate(executionCtrl *execution.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
in := new(execution.UpdateInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := request.GetExecutionNumberFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := executionCtrl.Update(ctx, session, spaceRef, pipelineUID, n, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, pipeline)
|
||||||
|
}
|
||||||
|
}
|
36
internal/api/handler/pipeline/create.go
Normal file
36
internal/api/handler/pipeline/create.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleCreate(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
in := new(pipeline.CreateInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := pipelineCtrl.Create(ctx, session, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusCreated, pipeline)
|
||||||
|
}
|
||||||
|
}
|
39
internal/api/handler/pipeline/delete.go
Normal file
39
internal/api/handler/pipeline/delete.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleDelete(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pipelineCtrl.Delete(ctx, session, spaceRef, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.DeleteSuccessful(w)
|
||||||
|
}
|
||||||
|
}
|
39
internal/api/handler/pipeline/find.go
Normal file
39
internal/api/handler/pipeline/find.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleFind(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := pipelineCtrl.Find(ctx, session, spaceRef, pipelineUID)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, pipeline)
|
||||||
|
}
|
||||||
|
}
|
48
internal/api/handler/pipeline/update.go
Normal file
48
internal/api/handler/pipeline/update.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleUpdate(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
in := new(pipeline.UpdateInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipelineRef, err := request.GetPipelineRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline, err := pipelineCtrl.Update(ctx, session, spaceRef, pipelineUID, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, pipeline)
|
||||||
|
}
|
||||||
|
}
|
@ -12,9 +12,7 @@ import (
|
|||||||
"github.com/harness/gitness/internal/api/request"
|
"github.com/harness/gitness/internal/api/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// HandleGetContent handles the get content HTTP API.
|
||||||
* Writes json-encoded content information to the http response body.
|
|
||||||
*/
|
|
||||||
func HandleGetContent(repoCtrl *repo.Controller) http.HandlerFunc {
|
func HandleGetContent(repoCtrl *repo.Controller) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
44
internal/api/handler/repo/content_paths_details.go
Normal file
44
internal/api/handler/repo/content_paths_details.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandlePathsDetails handles get file or directory details HTTP API.
|
||||||
|
func HandlePathsDetails(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(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gitRef := request.GetGitRefFromQueryOrDefault(r, "")
|
||||||
|
|
||||||
|
var in repo.PathsDetailsInput
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid request body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := repoCtrl.PathsDetails(ctx, session, repoRef, gitRef, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
@ -43,3 +43,26 @@ func HandleDiff(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||||||
render.JSONArrayDynamic(ctx, w, stream)
|
render.JSONArrayDynamic(ctx, w, stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleDiffStats how diff statistics of two commits, branches or tags.
|
||||||
|
func HandleDiffStats(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(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := request.GetOptionalRemainderFromPath(r)
|
||||||
|
|
||||||
|
output, err := repoCtrl.DiffStats(ctx, session, repoRef, path)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/harness/gitness/internal/api/controller/repo"
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
"github.com/harness/gitness/internal/api/render"
|
"github.com/harness/gitness/internal/api/render"
|
||||||
"github.com/harness/gitness/internal/api/request"
|
"github.com/harness/gitness/internal/api/request"
|
||||||
"github.com/harness/gitness/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -34,20 +33,15 @@ func HandleListCommits(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, renameDetails, err := repoCtrl.ListCommits(ctx, session, repoRef, gitRef, filter)
|
list, err := repoCtrl.ListCommits(ctx, session, repoRef, gitRef, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.TranslatedUserError(w, err)
|
render.TranslatedUserError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
commitsResponse := types.ListCommitResponse{
|
|
||||||
Commits: commits,
|
|
||||||
RenameDetails: renameDetails,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: get last page indicator explicitly - current check is wrong in case len % limit == 0
|
// TODO: get last page indicator explicitly - current check is wrong in case len % limit == 0
|
||||||
isLastPage := len(commits) < filter.Limit
|
isLastPage := len(list.Commits) < filter.Limit
|
||||||
render.PaginationNoTotal(r, w, filter.Page, filter.Limit, isLastPage)
|
render.PaginationNoTotal(r, w, filter.Page, filter.Limit, isLastPage)
|
||||||
render.JSON(w, http.StatusOK, commitsResponse)
|
render.JSON(w, http.StatusOK, list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
internal/api/handler/secret/create.go
Normal file
37
internal/api/handler/secret/create.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleCreate returns a http.HandlerFunc that creates a new secretsitory.
|
||||||
|
func HandleCreate(secretCtrl *secret.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
in := new(secret.CreateInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := secretCtrl.Create(ctx, session, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusCreated, secret.CopyWithoutData())
|
||||||
|
}
|
||||||
|
}
|
39
internal/api/handler/secret/delete.go
Normal file
39
internal/api/handler/secret/delete.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleDelete(secretCtrl *secret.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
secretRef, err := request.GetSecretRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = secretCtrl.Delete(ctx, session, spaceRef, secretUID)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.DeleteSuccessful(w)
|
||||||
|
}
|
||||||
|
}
|
39
internal/api/handler/secret/find.go
Normal file
39
internal/api/handler/secret/find.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleFind finds a secret from the database.
|
||||||
|
func HandleFind(secretCtrl *secret.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
secretRef, err := request.GetSecretRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := secretCtrl.Find(ctx, session, spaceRef, secretUID)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, secret.CopyWithoutData())
|
||||||
|
}
|
||||||
|
}
|
47
internal/api/handler/secret/update.go
Normal file
47
internal/api/handler/secret/update.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package secret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleUpdate(secretCtrl *secret.Controller) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
session, _ := request.AuthSessionFrom(ctx)
|
||||||
|
|
||||||
|
in := new(secret.UpdateInput)
|
||||||
|
err := json.NewDecoder(r.Body).Decode(in)
|
||||||
|
if err != nil {
|
||||||
|
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secretRef, err := request.GetSecretRefFromPath(r)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := secretCtrl.Update(ctx, session, spaceRef, secretUID, in)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, http.StatusOK, secret.CopyWithoutData())
|
||||||
|
}
|
||||||
|
}
|
35
internal/api/handler/space/list_pipelines.go
Normal file
35
internal/api/handler/space/list_pipelines.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleListPipelines(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(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := request.ParseListQueryFilterFromRequest(r)
|
||||||
|
repos, totalCount, err := spaceCtrl.ListPipelines(ctx, session, spaceRef, filter)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
|
||||||
|
render.JSON(w, http.StatusOK, repos)
|
||||||
|
}
|
||||||
|
}
|
42
internal/api/handler/space/list_secrets.go
Normal file
42
internal/api/handler/space/list_secrets.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
|
"github.com/harness/gitness/internal/api/render"
|
||||||
|
"github.com/harness/gitness/internal/api/request"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleListSecrets(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(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := request.ParseListQueryFilterFromRequest(r)
|
||||||
|
ret, totalCount, err := spaceCtrl.ListSecrets(ctx, session, spaceRef, filter)
|
||||||
|
if err != nil {
|
||||||
|
render.TranslatedUserError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip out data in the returned value
|
||||||
|
secrets := []types.Secret{}
|
||||||
|
for _, s := range ret {
|
||||||
|
secrets = append(secrets, *s.CopyWithoutData())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
|
||||||
|
render.JSON(w, http.StatusOK, secrets)
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,8 @@ func Generate() *openapi3.Spec {
|
|||||||
buildPrincipals(&reflector)
|
buildPrincipals(&reflector)
|
||||||
spaceOperations(&reflector)
|
spaceOperations(&reflector)
|
||||||
repoOperations(&reflector)
|
repoOperations(&reflector)
|
||||||
|
pipelineOperations(&reflector)
|
||||||
|
secretOperations(&reflector)
|
||||||
resourceOperations(&reflector)
|
resourceOperations(&reflector)
|
||||||
pullReqOperations(&reflector)
|
pullReqOperations(&reflector)
|
||||||
webhookOperations(&reflector)
|
webhookOperations(&reflector)
|
||||||
|
162
internal/api/openapi/pipeline.go
Normal file
162
internal/api/openapi/pipeline.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
|
"github.com/swaggest/openapi-go/openapi3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pipelineRequest struct {
|
||||||
|
Ref string `path:"pipeline_ref"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type executionRequest struct {
|
||||||
|
pipelineRequest
|
||||||
|
Number string `path:"execution_number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type createExecutionRequest struct {
|
||||||
|
pipelineRequest
|
||||||
|
execution.CreateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
type createPipelineRequest struct {
|
||||||
|
pipeline.CreateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
type getExecutionRequest struct {
|
||||||
|
executionRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type getPipelineRequest struct {
|
||||||
|
pipelineRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateExecutionRequest struct {
|
||||||
|
executionRequest
|
||||||
|
execution.UpdateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
type updatePipelineRequest struct {
|
||||||
|
pipelineRequest
|
||||||
|
pipeline.UpdateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func pipelineOperations(reflector *openapi3.Reflector) {
|
||||||
|
opCreate := openapi3.Operation{}
|
||||||
|
opCreate.WithTags("pipeline")
|
||||||
|
opCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createPipeline"})
|
||||||
|
_ = reflector.SetRequest(&opCreate, new(createPipelineRequest), http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(types.Pipeline), http.StatusCreated)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost, "/pipelines", opCreate)
|
||||||
|
|
||||||
|
opFind := openapi3.Operation{}
|
||||||
|
opFind.WithTags("pipeline")
|
||||||
|
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "findPipeline"})
|
||||||
|
_ = reflector.SetRequest(&opFind, new(getPipelineRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(types.Pipeline), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/pipelines/{pipeline_ref}", opFind)
|
||||||
|
|
||||||
|
opDelete := openapi3.Operation{}
|
||||||
|
opDelete.WithTags("pipeline")
|
||||||
|
opDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deletePipeline"})
|
||||||
|
_ = reflector.SetRequest(&opDelete, new(getPipelineRequest), http.MethodDelete)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, nil, http.StatusNoContent)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodDelete, "/pipelines/{pipeline_ref}", opDelete)
|
||||||
|
|
||||||
|
opUpdate := openapi3.Operation{}
|
||||||
|
opUpdate.WithTags("pipeline")
|
||||||
|
opUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updatePipeline"})
|
||||||
|
_ = reflector.SetRequest(&opUpdate, new(updatePipelineRequest), http.MethodPatch)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(types.Pipeline), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPatch,
|
||||||
|
"/pipelines/{pipeline_ref}", opUpdate)
|
||||||
|
|
||||||
|
executionCreate := openapi3.Operation{}
|
||||||
|
executionCreate.WithTags("pipeline")
|
||||||
|
executionCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createExecution"})
|
||||||
|
_ = reflector.SetRequest(&executionCreate, new(createExecutionRequest), http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&executionCreate, new(types.Execution), http.StatusCreated)
|
||||||
|
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost,
|
||||||
|
"/pipelines/{pipeline_ref}/executions", executionCreate)
|
||||||
|
|
||||||
|
executionFind := openapi3.Operation{}
|
||||||
|
executionFind.WithTags("pipeline")
|
||||||
|
executionFind.WithMapOfAnything(map[string]interface{}{"operationId": "findExecution"})
|
||||||
|
_ = reflector.SetRequest(&executionFind, new(getExecutionRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&executionFind, new(types.Execution), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet,
|
||||||
|
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionFind)
|
||||||
|
|
||||||
|
executionDelete := openapi3.Operation{}
|
||||||
|
executionDelete.WithTags("pipeline")
|
||||||
|
executionDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deleteExecution"})
|
||||||
|
_ = reflector.SetRequest(&executionDelete, new(getExecutionRequest), http.MethodDelete)
|
||||||
|
_ = reflector.SetJSONResponse(&executionDelete, nil, http.StatusNoContent)
|
||||||
|
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodDelete,
|
||||||
|
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionDelete)
|
||||||
|
|
||||||
|
executionUpdate := openapi3.Operation{}
|
||||||
|
executionUpdate.WithTags("pipeline")
|
||||||
|
executionUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updateExecution"})
|
||||||
|
_ = reflector.SetRequest(&executionUpdate, new(updateExecutionRequest), http.MethodPatch)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(types.Execution), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPatch,
|
||||||
|
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionUpdate)
|
||||||
|
|
||||||
|
executionList := openapi3.Operation{}
|
||||||
|
executionList.WithTags("pipeline")
|
||||||
|
executionList.WithMapOfAnything(map[string]interface{}{"operationId": "listExecutions"})
|
||||||
|
executionList.WithParameters(queryParameterPage, queryParameterLimit)
|
||||||
|
_ = reflector.SetRequest(&executionList, new(pipelineRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&executionList, []types.Execution{}, http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet,
|
||||||
|
"/pipelines/{pipeline_ref}/executions", executionList)
|
||||||
|
}
|
@ -57,6 +57,11 @@ type getContentRequest struct {
|
|||||||
Path string `path:"path"`
|
Path string `path:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pathsDetailsRequest struct {
|
||||||
|
repoRequest
|
||||||
|
repo.PathsDetailsInput
|
||||||
|
}
|
||||||
|
|
||||||
type getBlameRequest struct {
|
type getBlameRequest struct {
|
||||||
repoRequest
|
repoRequest
|
||||||
Path string `path:"path"`
|
Path string `path:"path"`
|
||||||
@ -481,6 +486,18 @@ func repoOperations(reflector *openapi3.Reflector) {
|
|||||||
_ = reflector.SetJSONResponse(&opGetContent, new(usererror.Error), http.StatusNotFound)
|
_ = reflector.SetJSONResponse(&opGetContent, new(usererror.Error), http.StatusNotFound)
|
||||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/content/{path}", opGetContent)
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/content/{path}", opGetContent)
|
||||||
|
|
||||||
|
opPathDetails := openapi3.Operation{}
|
||||||
|
opPathDetails.WithTags("repository")
|
||||||
|
opPathDetails.WithMapOfAnything(map[string]interface{}{"operationId": "pathDetails"})
|
||||||
|
opPathDetails.WithParameters(queryParameterGitRef)
|
||||||
|
_ = reflector.SetRequest(&opPathDetails, new(pathsDetailsRequest), http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&opPathDetails, new(repo.PathsDetailsOutput), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opPathDetails, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opPathDetails, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opPathDetails, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opPathDetails, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/path-details", opPathDetails)
|
||||||
|
|
||||||
opGetRaw := openapi3.Operation{}
|
opGetRaw := openapi3.Operation{}
|
||||||
opGetRaw.WithTags("repository")
|
opGetRaw.WithTags("repository")
|
||||||
opGetRaw.WithMapOfAnything(map[string]interface{}{"operationId": "getRaw"})
|
opGetRaw.WithMapOfAnything(map[string]interface{}{"operationId": "getRaw"})
|
||||||
@ -644,13 +661,23 @@ func repoOperations(reflector *openapi3.Reflector) {
|
|||||||
opDiff.WithTags("repository")
|
opDiff.WithTags("repository")
|
||||||
opDiff.WithMapOfAnything(map[string]interface{}{"operationId": "rawDiff"})
|
opDiff.WithMapOfAnything(map[string]interface{}{"operationId": "rawDiff"})
|
||||||
_ = reflector.SetRequest(&opDiff, new(getRawDiffRequest), http.MethodGet)
|
_ = reflector.SetRequest(&opDiff, new(getRawDiffRequest), http.MethodGet)
|
||||||
_ = reflector.SetJSONResponse(&opDiff, new(types.DiffStats), http.StatusOK)
|
|
||||||
_ = reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain")
|
_ = reflector.SetStringResponse(&opDiff, http.StatusOK, "text/plain")
|
||||||
|
_ = reflector.SetJSONResponse(&opDiff, []gitrpc.FileDiff{}, http.StatusOK)
|
||||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError)
|
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusInternalServerError)
|
||||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized)
|
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusUnauthorized)
|
||||||
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden)
|
_ = reflector.SetJSONResponse(&opDiff, new(usererror.Error), http.StatusForbidden)
|
||||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/diff/{range}", opDiff)
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/diff/{range}", opDiff)
|
||||||
|
|
||||||
|
opDiffStats := openapi3.Operation{}
|
||||||
|
opDiffStats.WithTags("repository")
|
||||||
|
opDiffStats.WithMapOfAnything(map[string]interface{}{"operationId": "diffStats"})
|
||||||
|
_ = reflector.SetRequest(&opDiffStats, new(getRawDiffRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opDiffStats, new(types.DiffStats), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opDiffStats, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opDiffStats, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opDiffStats, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/diff-stats/{range}", opDiffStats)
|
||||||
|
|
||||||
opMergeCheck := openapi3.Operation{}
|
opMergeCheck := openapi3.Operation{}
|
||||||
opMergeCheck.WithTags("repository")
|
opMergeCheck.WithTags("repository")
|
||||||
opMergeCheck.WithMapOfAnything(map[string]interface{}{"operationId": "mergeCheck"})
|
opMergeCheck.WithMapOfAnything(map[string]interface{}{"operationId": "mergeCheck"})
|
||||||
|
79
internal/api/openapi/secret.go
Normal file
79
internal/api/openapi/secret.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
|
"github.com/swaggest/openapi-go/openapi3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type createSecretRequest struct {
|
||||||
|
secret.CreateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
type secretRequest struct {
|
||||||
|
Ref string `path:"secret_ref"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type getSecretRequest struct {
|
||||||
|
secretRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateSecretRequest struct {
|
||||||
|
secretRequest
|
||||||
|
secret.UpdateInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretOperations(reflector *openapi3.Reflector) {
|
||||||
|
opCreate := openapi3.Operation{}
|
||||||
|
opCreate.WithTags("secret")
|
||||||
|
opCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createSecret"})
|
||||||
|
_ = reflector.SetRequest(&opCreate, new(createSecretRequest), http.MethodPost)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(types.Secret), http.StatusCreated)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPost, "/secrets", opCreate)
|
||||||
|
|
||||||
|
opFind := openapi3.Operation{}
|
||||||
|
opFind.WithTags("secret")
|
||||||
|
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "findSecret"})
|
||||||
|
_ = reflector.SetRequest(&opFind, new(getSecretRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(types.Secret), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/secrets/{secret_ref}", opFind)
|
||||||
|
|
||||||
|
opDelete := openapi3.Operation{}
|
||||||
|
opDelete.WithTags("secret")
|
||||||
|
opDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deleteSecret"})
|
||||||
|
_ = reflector.SetRequest(&opDelete, new(getSecretRequest), http.MethodDelete)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, nil, http.StatusNoContent)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodDelete, "/secrets/{secret_ref}", opDelete)
|
||||||
|
|
||||||
|
opUpdate := openapi3.Operation{}
|
||||||
|
opUpdate.WithTags("secret")
|
||||||
|
opUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updateSecret"})
|
||||||
|
_ = reflector.SetRequest(&opUpdate, new(updateSecretRequest), http.MethodPatch)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(types.Secret), http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusBadRequest)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodPatch, "/secrets/{secret_ref}", opUpdate)
|
||||||
|
}
|
@ -230,6 +230,30 @@ func spaceOperations(reflector *openapi3.Reflector) {
|
|||||||
_ = reflector.SetJSONResponse(&opRepos, new(usererror.Error), http.StatusNotFound)
|
_ = reflector.SetJSONResponse(&opRepos, new(usererror.Error), http.StatusNotFound)
|
||||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/repos", opRepos)
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/repos", opRepos)
|
||||||
|
|
||||||
|
opPipelines := openapi3.Operation{}
|
||||||
|
opPipelines.WithTags("space")
|
||||||
|
opPipelines.WithMapOfAnything(map[string]interface{}{"operationId": "listPipelines"})
|
||||||
|
opPipelines.WithParameters(queryParameterQueryRepo, queryParameterPage, queryParameterLimit)
|
||||||
|
_ = reflector.SetRequest(&opPipelines, new(spaceRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opPipelines, []types.Pipeline{}, http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/pipelines", opPipelines)
|
||||||
|
|
||||||
|
opSecrets := openapi3.Operation{}
|
||||||
|
opSecrets.WithTags("space")
|
||||||
|
opSecrets.WithMapOfAnything(map[string]interface{}{"operationId": "listSecrets"})
|
||||||
|
opSecrets.WithParameters(queryParameterQueryRepo, queryParameterPage, queryParameterLimit)
|
||||||
|
_ = reflector.SetRequest(&opSecrets, new(spaceRequest), http.MethodGet)
|
||||||
|
_ = reflector.SetJSONResponse(&opSecrets, []types.Secret{}, http.StatusOK)
|
||||||
|
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusInternalServerError)
|
||||||
|
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusUnauthorized)
|
||||||
|
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusForbidden)
|
||||||
|
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusNotFound)
|
||||||
|
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/secrets", opSecrets)
|
||||||
|
|
||||||
opServiceAccounts := openapi3.Operation{}
|
opServiceAccounts := openapi3.Operation{}
|
||||||
opServiceAccounts.WithTags("space")
|
opServiceAccounts.WithTags("space")
|
||||||
opServiceAccounts.WithMapOfAnything(map[string]interface{}{"operationId": "listServiceAccounts"})
|
opServiceAccounts.WithMapOfAnything(map[string]interface{}{"operationId": "listServiceAccounts"})
|
||||||
|
29
internal/api/request/pipeline.go
Normal file
29
internal/api/request/pipeline.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathParamPipelineRef = "pipeline_ref"
|
||||||
|
PathParamExecutionNumber = "execution_number"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPipelineRefFromPath(r *http.Request) (string, error) {
|
||||||
|
rawRef, err := PathParamOrError(r, PathParamPipelineRef)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// paths are unescaped
|
||||||
|
return url.PathUnescape(rawRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExecutionNumberFromPath(r *http.Request) (int64, error) {
|
||||||
|
return PathParamAsPositiveInt64(r, PathParamExecutionNumber)
|
||||||
|
}
|
@ -102,7 +102,7 @@ func ParsePullReqActivityFilter(r *http.Request) (*types.PullReqActivityFilter,
|
|||||||
|
|
||||||
// parsePullReqActivityKinds extracts the pull request activity kinds from the url.
|
// parsePullReqActivityKinds extracts the pull request activity kinds from the url.
|
||||||
func parsePullReqActivityKinds(r *http.Request) []enum.PullReqActivityKind {
|
func parsePullReqActivityKinds(r *http.Request) []enum.PullReqActivityKind {
|
||||||
strKinds := r.Form[QueryParamKind]
|
strKinds := r.URL.Query()[QueryParamKind]
|
||||||
m := make(map[enum.PullReqActivityKind]struct{}) // use map to eliminate duplicates
|
m := make(map[enum.PullReqActivityKind]struct{}) // use map to eliminate duplicates
|
||||||
for _, s := range strKinds {
|
for _, s := range strKinds {
|
||||||
if kind, ok := enum.PullReqActivityKind(s).Sanitize(); ok {
|
if kind, ok := enum.PullReqActivityKind(s).Sanitize(); ok {
|
||||||
@ -124,7 +124,7 @@ func parsePullReqActivityKinds(r *http.Request) []enum.PullReqActivityKind {
|
|||||||
|
|
||||||
// parsePullReqActivityTypes extracts the pull request activity types from the url.
|
// parsePullReqActivityTypes extracts the pull request activity types from the url.
|
||||||
func parsePullReqActivityTypes(r *http.Request) []enum.PullReqActivityType {
|
func parsePullReqActivityTypes(r *http.Request) []enum.PullReqActivityType {
|
||||||
strType := r.Form[QueryParamType]
|
strType := r.URL.Query()[QueryParamType]
|
||||||
m := make(map[enum.PullReqActivityType]struct{}) // use map to eliminate duplicates
|
m := make(map[enum.PullReqActivityType]struct{}) // use map to eliminate duplicates
|
||||||
for _, s := range strType {
|
for _, s := range strType {
|
||||||
if t, ok := enum.PullReqActivityType(s).Sanitize(); ok {
|
if t, ok := enum.PullReqActivityType(s).Sanitize(); ok {
|
||||||
|
24
internal/api/request/secret.go
Normal file
24
internal/api/request/secret.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2022 Harness Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Polyform Free Trial License
|
||||||
|
// that can be found in the LICENSE.md file for this repository.
|
||||||
|
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathParamSecretRef = "secret_ref"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSecretRefFromPath(r *http.Request) (string, error) {
|
||||||
|
rawRef, err := PathParamOrError(r, PathParamSecretRef)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// paths are unescaped
|
||||||
|
return url.PathUnescape(rawRef)
|
||||||
|
}
|
@ -9,6 +9,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/harness/gitness/internal/api/usererror"
|
"github.com/harness/gitness/internal/api/usererror"
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
"github.com/harness/gitness/types/enum"
|
"github.com/harness/gitness/types/enum"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
@ -17,11 +18,10 @@ import (
|
|||||||
const (
|
const (
|
||||||
PathParamRemainder = "*"
|
PathParamRemainder = "*"
|
||||||
|
|
||||||
QueryParamSort = "sort"
|
|
||||||
QueryParamOrder = "order"
|
|
||||||
QueryParamQuery = "query"
|
|
||||||
|
|
||||||
QueryParamCreatedBy = "created_by"
|
QueryParamCreatedBy = "created_by"
|
||||||
|
QueryParamSort = "sort"
|
||||||
|
QueryParamOrder = "order"
|
||||||
|
QueryParamQuery = "query"
|
||||||
|
|
||||||
QueryParamState = "state"
|
QueryParamState = "state"
|
||||||
QueryParamKind = "kind"
|
QueryParamKind = "kind"
|
||||||
@ -204,3 +204,19 @@ func ParseOrder(r *http.Request) enum.Order {
|
|||||||
func ParseSort(r *http.Request) string {
|
func ParseSort(r *http.Request) string {
|
||||||
return r.URL.Query().Get(QueryParamSort)
|
return r.URL.Query().Get(QueryParamSort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePaginationFromRequest parses pagination related info from the url.
|
||||||
|
func ParsePaginationFromRequest(r *http.Request) types.Pagination {
|
||||||
|
return types.Pagination{
|
||||||
|
Page: ParsePage(r),
|
||||||
|
Size: ParseLimit(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseListQueryFilterFromRequest parses pagination and query related info from the url.
|
||||||
|
func ParseListQueryFilterFromRequest(r *http.Request) types.ListQueryFilter {
|
||||||
|
return types.ListQueryFilter{
|
||||||
|
Query: ParseQuery(r),
|
||||||
|
Pagination: ParsePaginationFromRequest(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -63,6 +63,12 @@ func (a *MembershipAuthorizer) Check(
|
|||||||
case enum.ResourceTypeServiceAccount:
|
case enum.ResourceTypeServiceAccount:
|
||||||
spaceRef = scope.SpacePath
|
spaceRef = scope.SpacePath
|
||||||
|
|
||||||
|
case enum.ResourceTypePipeline:
|
||||||
|
spaceRef = scope.SpacePath
|
||||||
|
|
||||||
|
case enum.ResourceTypeSecret:
|
||||||
|
spaceRef = scope.SpacePath
|
||||||
|
|
||||||
case enum.ResourceTypeUser:
|
case enum.ResourceTypeUser:
|
||||||
// a user is allowed to view / edit themselves
|
// a user is allowed to view / edit themselves
|
||||||
if resource.Name == session.Principal.UID &&
|
if resource.Name == session.Principal.UID &&
|
||||||
|
@ -10,21 +10,27 @@ import (
|
|||||||
|
|
||||||
"github.com/harness/gitness/githook"
|
"github.com/harness/gitness/githook"
|
||||||
"github.com/harness/gitness/internal/api/controller/check"
|
"github.com/harness/gitness/internal/api/controller/check"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
controllergithook "github.com/harness/gitness/internal/api/controller/githook"
|
controllergithook "github.com/harness/gitness/internal/api/controller/githook"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
"github.com/harness/gitness/internal/api/controller/principal"
|
"github.com/harness/gitness/internal/api/controller/principal"
|
||||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||||
"github.com/harness/gitness/internal/api/controller/repo"
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||||
"github.com/harness/gitness/internal/api/controller/space"
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
"github.com/harness/gitness/internal/api/controller/user"
|
"github.com/harness/gitness/internal/api/controller/user"
|
||||||
"github.com/harness/gitness/internal/api/controller/webhook"
|
"github.com/harness/gitness/internal/api/controller/webhook"
|
||||||
"github.com/harness/gitness/internal/api/handler/account"
|
"github.com/harness/gitness/internal/api/handler/account"
|
||||||
handlercheck "github.com/harness/gitness/internal/api/handler/check"
|
handlercheck "github.com/harness/gitness/internal/api/handler/check"
|
||||||
|
handlerexecution "github.com/harness/gitness/internal/api/handler/execution"
|
||||||
handlergithook "github.com/harness/gitness/internal/api/handler/githook"
|
handlergithook "github.com/harness/gitness/internal/api/handler/githook"
|
||||||
|
handlerpipeline "github.com/harness/gitness/internal/api/handler/pipeline"
|
||||||
handlerprincipal "github.com/harness/gitness/internal/api/handler/principal"
|
handlerprincipal "github.com/harness/gitness/internal/api/handler/principal"
|
||||||
handlerpullreq "github.com/harness/gitness/internal/api/handler/pullreq"
|
handlerpullreq "github.com/harness/gitness/internal/api/handler/pullreq"
|
||||||
handlerrepo "github.com/harness/gitness/internal/api/handler/repo"
|
handlerrepo "github.com/harness/gitness/internal/api/handler/repo"
|
||||||
"github.com/harness/gitness/internal/api/handler/resource"
|
"github.com/harness/gitness/internal/api/handler/resource"
|
||||||
|
handlersecret "github.com/harness/gitness/internal/api/handler/secret"
|
||||||
handlerserviceaccount "github.com/harness/gitness/internal/api/handler/serviceaccount"
|
handlerserviceaccount "github.com/harness/gitness/internal/api/handler/serviceaccount"
|
||||||
handlerspace "github.com/harness/gitness/internal/api/handler/space"
|
handlerspace "github.com/harness/gitness/internal/api/handler/space"
|
||||||
"github.com/harness/gitness/internal/api/handler/system"
|
"github.com/harness/gitness/internal/api/handler/system"
|
||||||
@ -54,7 +60,7 @@ type APIHandler interface {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// terminatedPathPrefixesAPI is the list of prefixes that will require resolving terminated paths.
|
// terminatedPathPrefixesAPI is the list of prefixes that will require resolving terminated paths.
|
||||||
terminatedPathPrefixesAPI = []string{"/v1/spaces/", "/v1/repos/"}
|
terminatedPathPrefixesAPI = []string{"/v1/spaces/", "/v1/repos/", "/v1/pipelines/", "/v1/secrets/"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAPIHandler returns a new APIHandler.
|
// NewAPIHandler returns a new APIHandler.
|
||||||
@ -62,7 +68,10 @@ func NewAPIHandler(
|
|||||||
config *types.Config,
|
config *types.Config,
|
||||||
authenticator authn.Authenticator,
|
authenticator authn.Authenticator,
|
||||||
repoCtrl *repo.Controller,
|
repoCtrl *repo.Controller,
|
||||||
|
executionCtrl *execution.Controller,
|
||||||
spaceCtrl *space.Controller,
|
spaceCtrl *space.Controller,
|
||||||
|
pipelineCtrl *pipeline.Controller,
|
||||||
|
secretCtrl *secret.Controller,
|
||||||
pullreqCtrl *pullreq.Controller,
|
pullreqCtrl *pullreq.Controller,
|
||||||
webhookCtrl *webhook.Controller,
|
webhookCtrl *webhook.Controller,
|
||||||
githookCtrl *controllergithook.Controller,
|
githookCtrl *controllergithook.Controller,
|
||||||
@ -92,7 +101,7 @@ func NewAPIHandler(
|
|||||||
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterAPI))
|
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterAPI))
|
||||||
|
|
||||||
r.Route("/v1", func(r chi.Router) {
|
r.Route("/v1", func(r chi.Router) {
|
||||||
setupRoutesV1(r, repoCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, githookCtrl,
|
setupRoutesV1(r, repoCtrl, executionCtrl, pipelineCtrl, secretCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, githookCtrl,
|
||||||
saCtrl, userCtrl, principalCtrl, checkCtrl)
|
saCtrl, userCtrl, principalCtrl, checkCtrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -115,6 +124,9 @@ func corsHandler(config *types.Config) func(http.Handler) http.Handler {
|
|||||||
|
|
||||||
func setupRoutesV1(r chi.Router,
|
func setupRoutesV1(r chi.Router,
|
||||||
repoCtrl *repo.Controller,
|
repoCtrl *repo.Controller,
|
||||||
|
executionCtrl *execution.Controller,
|
||||||
|
pipelineCtrl *pipeline.Controller,
|
||||||
|
secretCtrl *secret.Controller,
|
||||||
spaceCtrl *space.Controller,
|
spaceCtrl *space.Controller,
|
||||||
pullreqCtrl *pullreq.Controller,
|
pullreqCtrl *pullreq.Controller,
|
||||||
webhookCtrl *webhook.Controller,
|
webhookCtrl *webhook.Controller,
|
||||||
@ -126,6 +138,8 @@ func setupRoutesV1(r chi.Router,
|
|||||||
) {
|
) {
|
||||||
setupSpaces(r, spaceCtrl)
|
setupSpaces(r, spaceCtrl)
|
||||||
setupRepos(r, repoCtrl, pullreqCtrl, webhookCtrl, checkCtrl)
|
setupRepos(r, repoCtrl, pullreqCtrl, webhookCtrl, checkCtrl)
|
||||||
|
setupPipelines(r, pipelineCtrl, executionCtrl)
|
||||||
|
setupSecrets(r, secretCtrl)
|
||||||
setupUser(r, userCtrl)
|
setupUser(r, userCtrl)
|
||||||
setupServiceAccounts(r, saCtrl)
|
setupServiceAccounts(r, saCtrl)
|
||||||
setupPrincipals(r, principalCtrl)
|
setupPrincipals(r, principalCtrl)
|
||||||
@ -151,6 +165,8 @@ func setupSpaces(r chi.Router, spaceCtrl *space.Controller) {
|
|||||||
r.Get("/spaces", handlerspace.HandleListSpaces(spaceCtrl))
|
r.Get("/spaces", handlerspace.HandleListSpaces(spaceCtrl))
|
||||||
r.Get("/repos", handlerspace.HandleListRepos(spaceCtrl))
|
r.Get("/repos", handlerspace.HandleListRepos(spaceCtrl))
|
||||||
r.Get("/service-accounts", handlerspace.HandleListServiceAccounts(spaceCtrl))
|
r.Get("/service-accounts", handlerspace.HandleListServiceAccounts(spaceCtrl))
|
||||||
|
r.Get("/pipelines", handlerspace.HandleListPipelines(spaceCtrl))
|
||||||
|
r.Get("/secrets", handlerspace.HandleListSecrets(spaceCtrl))
|
||||||
|
|
||||||
// Child collections
|
// Child collections
|
||||||
r.Route("/paths", func(r chi.Router) {
|
r.Route("/paths", func(r chi.Router) {
|
||||||
@ -200,6 +216,8 @@ func setupRepos(r chi.Router,
|
|||||||
r.Get("/*", handlerrepo.HandleGetContent(repoCtrl))
|
r.Get("/*", handlerrepo.HandleGetContent(repoCtrl))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Post("/path-details", handlerrepo.HandlePathsDetails(repoCtrl))
|
||||||
|
|
||||||
r.Route("/blame", func(r chi.Router) {
|
r.Route("/blame", func(r chi.Router) {
|
||||||
r.Get("/*", handlerrepo.HandleBlame(repoCtrl))
|
r.Get("/*", handlerrepo.HandleBlame(repoCtrl))
|
||||||
})
|
})
|
||||||
@ -253,6 +271,9 @@ func setupRepos(r chi.Router,
|
|||||||
r.Route("/diff", func(r chi.Router) {
|
r.Route("/diff", func(r chi.Router) {
|
||||||
r.Get("/*", handlerrepo.HandleDiff(repoCtrl))
|
r.Get("/*", handlerrepo.HandleDiff(repoCtrl))
|
||||||
})
|
})
|
||||||
|
r.Route("/diff-stats", func(r chi.Router) {
|
||||||
|
r.Get("/*", handlerrepo.HandleDiffStats(repoCtrl))
|
||||||
|
})
|
||||||
r.Route("/merge-check", func(r chi.Router) {
|
r.Route("/merge-check", func(r chi.Router) {
|
||||||
r.Post("/*", handlerrepo.HandleMergeCheck(repoCtrl))
|
r.Post("/*", handlerrepo.HandleMergeCheck(repoCtrl))
|
||||||
})
|
})
|
||||||
@ -266,6 +287,43 @@ func setupRepos(r chi.Router,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupPipelines(r chi.Router, pipelineCtrl *pipeline.Controller, executionCtrl *execution.Controller) {
|
||||||
|
r.Route("/pipelines", func(r chi.Router) {
|
||||||
|
// Create takes path and parentId via body, not uri
|
||||||
|
r.Post("/", handlerpipeline.HandleCreate(pipelineCtrl))
|
||||||
|
r.Route(fmt.Sprintf("/{%s}", request.PathParamPipelineRef), func(r chi.Router) {
|
||||||
|
r.Get("/", handlerpipeline.HandleFind(pipelineCtrl))
|
||||||
|
r.Patch("/", handlerpipeline.HandleUpdate(pipelineCtrl))
|
||||||
|
r.Delete("/", handlerpipeline.HandleDelete(pipelineCtrl))
|
||||||
|
setupExecutions(r, pipelineCtrl, executionCtrl)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupSecrets(r chi.Router, secretCtrl *secret.Controller) {
|
||||||
|
r.Route("/secrets", func(r chi.Router) {
|
||||||
|
// Create takes path and parentId via body, not uri
|
||||||
|
r.Post("/", handlersecret.HandleCreate(secretCtrl))
|
||||||
|
r.Route(fmt.Sprintf("/{%s}", request.PathParamSecretRef), func(r chi.Router) {
|
||||||
|
r.Get("/", handlersecret.HandleFind(secretCtrl))
|
||||||
|
r.Patch("/", handlersecret.HandleUpdate(secretCtrl))
|
||||||
|
r.Delete("/", handlersecret.HandleDelete(secretCtrl))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupExecutions(r chi.Router, pipelineCtrl *pipeline.Controller, executionCtrl *execution.Controller) {
|
||||||
|
r.Route("/executions", func(r chi.Router) {
|
||||||
|
r.Get("/", handlerexecution.HandleList(executionCtrl))
|
||||||
|
r.Post("/", handlerexecution.HandleCreate(executionCtrl))
|
||||||
|
r.Route(fmt.Sprintf("/{%s}", request.PathParamExecutionNumber), func(r chi.Router) {
|
||||||
|
r.Get("/", handlerexecution.HandleFind(executionCtrl))
|
||||||
|
r.Patch("/", handlerexecution.HandleUpdate(executionCtrl))
|
||||||
|
r.Delete("/", handlerexecution.HandleDelete(executionCtrl))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller) {
|
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller) {
|
||||||
r.Route("/internal", func(r chi.Router) {
|
r.Route("/internal", func(r chi.Router) {
|
||||||
SetupGitHooks(r, githookCtrl)
|
SetupGitHooks(r, githookCtrl)
|
||||||
|
@ -7,10 +7,13 @@ package router
|
|||||||
import (
|
import (
|
||||||
"github.com/harness/gitness/gitrpc"
|
"github.com/harness/gitness/gitrpc"
|
||||||
"github.com/harness/gitness/internal/api/controller/check"
|
"github.com/harness/gitness/internal/api/controller/check"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/execution"
|
||||||
"github.com/harness/gitness/internal/api/controller/githook"
|
"github.com/harness/gitness/internal/api/controller/githook"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||||
"github.com/harness/gitness/internal/api/controller/principal"
|
"github.com/harness/gitness/internal/api/controller/principal"
|
||||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||||
"github.com/harness/gitness/internal/api/controller/repo"
|
"github.com/harness/gitness/internal/api/controller/repo"
|
||||||
|
"github.com/harness/gitness/internal/api/controller/secret"
|
||||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||||
"github.com/harness/gitness/internal/api/controller/space"
|
"github.com/harness/gitness/internal/api/controller/space"
|
||||||
"github.com/harness/gitness/internal/api/controller/user"
|
"github.com/harness/gitness/internal/api/controller/user"
|
||||||
@ -57,7 +60,10 @@ func ProvideAPIHandler(
|
|||||||
config *types.Config,
|
config *types.Config,
|
||||||
authenticator authn.Authenticator,
|
authenticator authn.Authenticator,
|
||||||
repoCtrl *repo.Controller,
|
repoCtrl *repo.Controller,
|
||||||
|
executionCtrl *execution.Controller,
|
||||||
spaceCtrl *space.Controller,
|
spaceCtrl *space.Controller,
|
||||||
|
pipelineCtrl *pipeline.Controller,
|
||||||
|
secretCtrl *secret.Controller,
|
||||||
pullreqCtrl *pullreq.Controller,
|
pullreqCtrl *pullreq.Controller,
|
||||||
webhookCtrl *webhook.Controller,
|
webhookCtrl *webhook.Controller,
|
||||||
githookCtrl *githook.Controller,
|
githookCtrl *githook.Controller,
|
||||||
@ -66,8 +72,8 @@ func ProvideAPIHandler(
|
|||||||
principalCtrl principal.Controller,
|
principalCtrl principal.Controller,
|
||||||
checkCtrl *check.Controller,
|
checkCtrl *check.Controller,
|
||||||
) APIHandler {
|
) APIHandler {
|
||||||
return NewAPIHandler(config, authenticator, repoCtrl, spaceCtrl, pullreqCtrl,
|
return NewAPIHandler(config, authenticator, repoCtrl, executionCtrl, spaceCtrl, pipelineCtrl, secretCtrl,
|
||||||
webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl)
|
pullreqCtrl, webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideWebHandler(config *types.Config) WebHandler {
|
func ProvideWebHandler(config *types.Config) WebHandler {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user