Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: KBC ignore, pull command #2136

Merged
merged 11 commits into from
Nov 19, 2024
Merged
2 changes: 2 additions & 0 deletions internal/pkg/model/objectstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ type ConfigState struct {
*ConfigManifest
Remote *Config
Local *Config
Ignore bool
}

type ConfigRowState struct {
*ConfigRowManifest
Remote *ConfigRow
Local *ConfigRow
Ignore bool
}

// ToAPIObjectKey ...
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/plan/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/keboola/keboola-as-code/internal/pkg/utils/errors"
)

func NewPlan(diffResults *diff.Results, allowTargetEnv bool) (*diffop.Plan, error) {
func NewPlan(diffResults *diff.Results) (*diffop.Plan, error) {
plan := diffop.NewPlan(`push`)
for _, result := range diffResults.Results {
switch result.State {
Expand Down
36 changes: 36 additions & 0 deletions internal/pkg/project/ignore/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ignore

import (
"context"

"github.com/keboola/keboola-as-code/internal/pkg/filesystem"
"github.com/keboola/keboola-as-code/internal/pkg/state"
"github.com/keboola/keboola-as-code/internal/pkg/utils/errors"
)

const KBCIgnoreFilePath = ".keboola/kbc_ignore"

type File struct {
rawStringPattern string
state *state.Registry
}

func newFile(pattern string, state *state.Registry) *File {
return &File{
rawStringPattern: pattern,
state: state,
}
}

func LoadFile(ctx context.Context, fs filesystem.Fs, state *state.Registry, path string) (*File, error) {
if !fs.Exists(ctx, path) {
return nil, errors.Errorf("ignore file \"%s\" not found", path)
}

content, err := fs.ReadFile(ctx, filesystem.NewFileDef(path))
if err != nil {
return nil, err
}

return newFile(content.Content, state), nil
}
104 changes: 104 additions & 0 deletions internal/pkg/project/ignore/file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package ignore

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/keboola/keboola-as-code/internal/pkg/filesystem"
"github.com/keboola/keboola-as-code/internal/pkg/filesystem/aferofs"
"github.com/keboola/keboola-as-code/internal/pkg/filesystem/knownpaths"
"github.com/keboola/keboola-as-code/internal/pkg/model"
"github.com/keboola/keboola-as-code/internal/pkg/naming"
"github.com/keboola/keboola-as-code/internal/pkg/state/registry"
)

func Test_loadFile(t *testing.T) {
t.Parallel()
ctx := context.Background()

projectState := newTestRegistry(t)

fs := aferofs.NewMemoryFs()
require.NoError(t, fs.WriteFile(ctx, filesystem.NewRawFile(`foo/bar1`, "keboola.bar/678/34\nkeboola.foo/345")))

file, err := LoadFile(ctx, fs, projectState, "foo/bar1")
require.NoError(t, err)

assert.Equal(t, "keboola.bar/678/34\nkeboola.foo/345", file.rawStringPattern)
}

func newTestRegistry(t *testing.T) *registry.Registry {
t.Helper()

r := registry.New(knownpaths.NewNop(context.Background()), naming.NewRegistry(), model.NewComponentsMap(nil), model.SortByPath)
// Branch 1
branch1Key := model.BranchKey{ID: 123}
branch1 := &model.BranchState{
BranchManifest: &model.BranchManifest{
BranchKey: branch1Key,
},
Local: &model.Branch{
Name: "Main",
IsDefault: true,
},
}
assert.NoError(t, r.Set(branch1))

// Branch 2
branch2Key := model.BranchKey{ID: 567}
branch2 := &model.BranchState{
BranchManifest: &model.BranchManifest{
BranchKey: branch2Key,
},
Local: &model.Branch{
Name: "Foo Bar Branch",
IsDefault: false,
},
}
assert.NoError(t, r.Set(branch2))

// Config 1
config1Key := model.ConfigKey{BranchID: 123, ComponentID: "keboola.foo", ID: `345`}
config1 := &model.ConfigState{
ConfigManifest: &model.ConfigManifest{ConfigKey: config1Key},
Local: &model.Config{
Name: "Config 1",
},
}
assert.NoError(t, r.Set(config1))

// Config 2
config2Key := model.ConfigKey{BranchID: 123, ComponentID: "keboola.bar", ID: `678`}
config2 := &model.ConfigState{
ConfigManifest: &model.ConfigManifest{ConfigKey: config2Key},
Local: &model.Config{
Name: "Config 2",
},
}
assert.NoError(t, r.Set(config2))

// Config Row 1
row1Key := model.ConfigRowKey{BranchID: 123, ComponentID: "keboola.bar", ConfigID: `678`, ID: `12`}
row1 := &model.ConfigRowState{
ConfigRowManifest: &model.ConfigRowManifest{ConfigRowKey: row1Key},
Local: &model.ConfigRow{
Name: "Config Row 1",
},
}
assert.NoError(t, r.Set(row1))

// Config Row 2
row2Key := model.ConfigRowKey{BranchID: 123, ComponentID: "keboola.bar", ConfigID: `678`, ID: `34`}
row2 := &model.ConfigRowState{
ConfigRowManifest: &model.ConfigRowManifest{ConfigRowKey: row2Key},
Local: &model.ConfigRow{
Name: "Config Row 2",
},
}
assert.NoError(t, r.Set(row2))

return r
}
55 changes: 55 additions & 0 deletions internal/pkg/project/ignore/ignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package ignore

import (
"strings"

"github.com/keboola/keboola-as-code/internal/pkg/utils/errors"
)

func (f *File) IgnoreConfigsOrRows() error {
return f.applyIgnoredPatterns()
}

// applyIgnoredPatterns parses the content for ignore patterns and applies them to configurations or rows.
func (f *File) applyIgnoredPatterns() error {
for _, pattern := range f.parseIgnoredPatterns() {
if err := f.applyIgnorePattern(pattern); err != nil {
continue
}
}
return nil
}

func (f *File) parseIgnoredPatterns() []string {
var ignorePatterns []string
lines := strings.Split(f.rawStringPattern, "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
// Skip empty lines and comments
if trimmedLine != "" && !strings.HasPrefix(trimmedLine, "#") {
ignorePatterns = append(ignorePatterns, trimmedLine)
}
}

return ignorePatterns
}

// applyIgnorePattern applies a single ignore pattern, marking the appropriate config or row as ignored.
func (f *File) applyIgnorePattern(ignoreConfig string) error {
parts := strings.Split(ignoreConfig, "/")

switch len(parts) {
case 2:
// Ignore config by ID and name.
configID, componentID := parts[1], parts[0]
f.state.IgnoreConfig(configID, componentID)
case 3:
// Ignore specific config row.
configID, rowID := parts[1], parts[2]
f.state.IgnoreConfigRow(configID, rowID)
default:
return errors.Errorf("invalid ignore ignoreConfig format: %s", ignoreConfig)
}

return nil
}
163 changes: 163 additions & 0 deletions internal/pkg/project/ignore/ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package ignore

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/keboola/keboola-as-code/internal/pkg/filesystem"
"github.com/keboola/keboola-as-code/internal/pkg/filesystem/aferofs"
)

func TestFile_IgnoreConfigsOrRows(t *testing.T) {
t.Parallel()

ctx := context.Background()
registry := newTestRegistry(t)
fs := aferofs.NewMemoryFs()

require.NoError(t, fs.WriteFile(ctx, filesystem.NewRawFile(`foo/bar1`, "keboola.bar/678/34\nkeboola.foo/345")))

file, err := LoadFile(ctx, fs, registry, "foo/bar1")
require.NoError(t, err)

assert.NoError(t, file.IgnoreConfigsOrRows())

assert.Len(t, registry.IgnoredConfigRows(), 1)
assert.Len(t, registry.IgnoredConfigs(), 1)
assert.Equal(t, registry.IgnoredConfigRows()[0].ID.String(), "34")
assert.Equal(t, registry.IgnoredConfigs()[0].ID.String(), "345")
}

func Test_applyIgnoredPatterns(t *testing.T) {
t.Parallel()
projectState := newTestRegistry(t)

type args struct {
pattern string
}

file := &File{
state: projectState,
}

tests := []struct {
name string
args args
wantErr assert.ErrorAssertionFunc
}{
{
name: "empty patterns",
args: args{
pattern: "",
},
wantErr: assert.Error,
},
{
name: "wrong pattern",
args: args{
pattern: "wrong pattern",
},
wantErr: assert.Error,
},
{
name: "too long pattern",
args: args{
pattern: "keboola.bar/687/1234/1234",
},
wantErr: assert.Error,
},
{
name: "short pattern",
args: args{
pattern: "keboola.bar",
},
wantErr: assert.Error,
},
{
name: "correct pattern",
args: args{
pattern: "keboola.bar/687",
},
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tt.wantErr(t, file.applyIgnorePattern(tt.args.pattern), fmt.Sprintf("applyIgnoredPatterns(%v)", tt.args.pattern))
})
}
}

func Test_parseIgnoredPatterns(t *testing.T) {
t.Parallel()

projectState := newTestRegistry(t)

type args struct {
content string
}
tests := []struct {
name string
args args
want []string
}{
{
name: "with comments",
args: args{
content: "##dataapps\nkeboola.data-apps/12345\n##dataapps2\nkeboola.data-apps/123/rowId123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123/rowId123",
"keboola.data-apps/12",
},
},
{
name: "with empty lines",
args: args{
content: "##dataapps\n\n\n\n\n\nkeboola.data-apps/12345\n\t##dataapps2\nkeboola.data-apps/123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123",
"keboola.data-apps/12",
},
},
{
name: "empty file",
args: args{
content: "\n",
},
want: nil,
},
{
name: "error",
args: args{
content: "##dataapps\n\n\n\n\n\nkeboola.data-apps/12345\n\t##dataapps2\nkeboola.data-apps/123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123",
"keboola.data-apps/12",
},
},
{
name: "empty file",
args: args{
content: "\n",
},
want: nil,
},
}

for _, tt := range tests {
file := newFile(tt.args.content, projectState)
got := file.parseIgnoredPatterns()
assert.Equal(t, tt.want, got)
}
}
Loading