Skip to content

Commit 197b49f

Browse files
Merge pull request #2507 from keboola/jt-claude-note
feat: Enable nestif linter
2 parents 1899dad + eb9246f commit 197b49f

53 files changed

Lines changed: 1496 additions & 1269 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ Services auto-reload on code changes using Air.
151151
- **Error wrapping**: Use error wrapping with stack traces; custom error types for domain logic
152152
- **Dependency management**: Constructor-based DI; interface segregation (small interfaces)
153153
- **Observability**: Structured logging; OpenTelemetry integration; metrics for critical paths
154+
- **Early returns**: Prefer early `return` / `continue` to reduce nesting
154155

155156
### Testing
156157
- Test files use `*_test.go` suffix and are located next to implementation

build/ci/golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ linters:
5858
- makezero
5959
- musttag
6060
- nakedret
61+
- nestif
6162
- nilerr
6263
- noctx
6364
- paralleltest

internal/pkg/diff/diff.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,21 @@ func (d *Differ) Diff() (*Results, error) {
9595
result, err := d.diffState(objectState)
9696
if err != nil {
9797
d.errors.Append(err)
98-
} else {
99-
if result.State != ResultEqual {
100-
results.Equal = false
101-
}
102-
if result.State == ResultNotEqual {
103-
results.HasNotEqualResult = true
104-
}
105-
if result.State != ResultOnlyInRemote {
106-
results.HasOnlyInRemoteResult = true
107-
}
108-
if result.State != ResultOnlyInLocal {
109-
results.HasOnlyInLocalResult = true
110-
}
111-
d.results = append(d.results, result)
98+
continue
99+
}
100+
if result.State != ResultEqual {
101+
results.Equal = false
102+
}
103+
if result.State == ResultNotEqual {
104+
results.HasNotEqualResult = true
105+
}
106+
if result.State != ResultOnlyInRemote {
107+
results.HasOnlyInRemoteResult = true
108+
}
109+
if result.State != ResultOnlyInLocal {
110+
results.HasOnlyInLocalResult = true
112111
}
112+
d.results = append(d.results, result)
113113
}
114114

115115
// Sort results

internal/pkg/diff/reporter.go

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,47 +43,49 @@ func (r *Reporter) PushStep(ps cmp.PathStep) {
4343
}
4444

4545
func (r *Reporter) Report(rs cmp.Result) {
46-
if !rs.Equal() {
47-
var lines []string
48-
remoteValue, localValue := r.path.Last().Values()
49-
50-
// Diff values
51-
if v, ok := r.relationsDiff(remoteValue, localValue); ok {
52-
// Relations diff
53-
lines = append(lines, v...)
54-
} else if v, ok := r.stringsDiff(remoteValue, localValue); ok {
55-
// Strings diff
56-
lines = append(lines, v...)
57-
} else {
58-
// Other types
59-
lines = append(lines, valuesDiff(remoteValue, localValue)...)
60-
}
46+
if rs.Equal() {
47+
return
48+
}
6149

62-
// Format lines
63-
var mark string
64-
switch {
65-
case remoteValue.IsValid() && !localValue.IsValid():
66-
mark = OnlyInRemoteMark
67-
case !remoteValue.IsValid() && localValue.IsValid():
68-
mark = OnlyInLocalMark
69-
default:
70-
// Values are present in both: local and remote value
71-
// Individual lines are already marked.
72-
mark = " "
73-
}
74-
pathStr := r.pathToString(r.path)
75-
if len(pathStr) > 0 {
76-
r.paths = append(r.paths, pathStr)
77-
if !r.isPathHidden() {
78-
r.diffs = append(r.diffs, fmt.Sprintf(`%s %s:`, mark, pathStr))
79-
// Ident values inside path
80-
mark += ` `
81-
}
82-
}
83-
for _, line := range lines {
84-
r.diffs = append(r.diffs, fmt.Sprintf(`%s %s`, mark, line))
50+
var lines []string
51+
remoteValue, localValue := r.path.Last().Values()
52+
53+
// Diff values
54+
if v, ok := r.relationsDiff(remoteValue, localValue); ok {
55+
// Relations diff
56+
lines = append(lines, v...)
57+
} else if v, ok := r.stringsDiff(remoteValue, localValue); ok {
58+
// Strings diff
59+
lines = append(lines, v...)
60+
} else {
61+
// Other types
62+
lines = append(lines, valuesDiff(remoteValue, localValue)...)
63+
}
64+
65+
// Format lines
66+
var mark string
67+
switch {
68+
case remoteValue.IsValid() && !localValue.IsValid():
69+
mark = OnlyInRemoteMark
70+
case !remoteValue.IsValid() && localValue.IsValid():
71+
mark = OnlyInLocalMark
72+
default:
73+
// Values are present in both: local and remote value
74+
// Individual lines are already marked.
75+
mark = " "
76+
}
77+
pathStr := r.pathToString(r.path)
78+
if len(pathStr) > 0 {
79+
r.paths = append(r.paths, pathStr)
80+
if !r.isPathHidden() {
81+
r.diffs = append(r.diffs, fmt.Sprintf(`%s %s:`, mark, pathStr))
82+
// Ident values inside path
83+
mark += ` `
8584
}
8685
}
86+
for _, line := range lines {
87+
r.diffs = append(r.diffs, fmt.Sprintf(`%s %s`, mark, line))
88+
}
8789
}
8890

8991
func (r *Reporter) PopStep() {
@@ -185,15 +187,22 @@ func (r *Reporter) pathToString(path cmp.Path) string {
185187
}
186188

187189
func (r *Reporter) objectPath(value reflect.Value) string {
188-
if value.IsValid() {
189-
if v, ok := value.Interface().(model.RecordPaths); ok {
190-
objectPath := v.Path()
191-
if objectPath != "" {
192-
if v, err := filesystem.Rel(r.manifest.Path(), objectPath); err == nil {
193-
return v
194-
}
195-
}
196-
}
190+
if !value.IsValid() {
191+
return ""
192+
}
193+
194+
v, ok := value.Interface().(model.RecordPaths)
195+
if !ok {
196+
return ""
197+
}
198+
199+
objectPath := v.Path()
200+
if objectPath == "" {
201+
return ""
202+
}
203+
204+
if v, err := filesystem.Rel(r.manifest.Path(), objectPath); err == nil {
205+
return v
197206
}
198207
return ""
199208
}

internal/pkg/mapper/datagateway/remote_load.go

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -63,52 +63,55 @@ func (m *dataGatewayMapper) AfterRemoteOperation(ctx context.Context, changes *m
6363

6464
// Check if workspace was created and details were set
6565
workspaceID, found, _ = config.Content.GetNested("parameters.db.workspaceId")
66-
if found && workspaceID != nil && workspaceID != "" {
67-
// Update the configuration in remote with workspace details
68-
// If this update fails, the workspace exists but the config doesn't reference it.
69-
// This creates an inconsistent state, so we return the error to prevent silent failures.
70-
api := m.KeboolaProjectAPI()
71-
changedFields := model.NewChangedFields()
72-
changedFields.Add("configuration")
73-
apiObject, apiChangedFields := config.ToAPIObject("Workspace created and configuration updated", changedFields)
74-
_, err := api.UpdateRequest(apiObject, apiChangedFields).Send(ctx)
75-
if err != nil {
76-
// Return error instead of logging and continuing.
77-
// The workspace has been created and should be used, but the config update failed.
78-
// This error must be propagated to prevent inconsistent state.
79-
errs.Append(errors.Errorf(`cannot update configuration "%s" with workspace details: %w`, config.Name, err))
80-
continue
66+
if !found || workspaceID == nil || workspaceID == "" {
67+
continue
68+
}
69+
70+
// Update the configuration in remote with workspace details
71+
// If this update fails, the workspace exists but the config doesn't reference it.
72+
// This creates an inconsistent state, so we return the error to prevent silent failures.
73+
api := m.KeboolaProjectAPI()
74+
changedFields := model.NewChangedFields()
75+
changedFields.Add("configuration")
76+
apiObject, apiChangedFields := config.ToAPIObject("Workspace created and configuration updated", changedFields)
77+
_, err := api.UpdateRequest(apiObject, apiChangedFields).Send(ctx)
78+
if err != nil {
79+
// Return error instead of logging and continuing.
80+
// The workspace has been created and should be used, but the config update failed.
81+
// This error must be propagated to prevent inconsistent state.
82+
errs.Append(errors.Errorf(`cannot update configuration "%s" with workspace details: %w`, config.Name, err))
83+
continue
84+
}
85+
m.logger.Debugf(ctx, `Updated configuration "%s" with workspace details`, config.Name)
86+
87+
// Update remote state with the updated config
88+
configState.SetRemoteState(config)
89+
90+
// Sync workspace details to local state if it exists
91+
if configState.Local == nil {
92+
continue
93+
}
94+
if err := configState.Local.Content.SetNested("parameters.db.workspaceId", workspaceID); err != nil {
95+
m.logger.Warnf(ctx, `Failed to sync workspaceId to local state for config "%s": %s`, config.Name, err.Error())
96+
}
97+
normalizeWorkspaceID(configState.Local)
98+
// Also sync other workspace details
99+
if host, found, _ := config.Content.GetNested("parameters.db.host"); found {
100+
if err := configState.Local.Content.SetNested("parameters.db.host", host); err != nil {
101+
m.logger.Warnf(ctx, `Failed to sync host to local state for config "%s": %s`, config.Name, err.Error())
102+
}
103+
}
104+
if user, found, _ := config.Content.GetNested("parameters.db.user"); found {
105+
if err := configState.Local.Content.SetNested("parameters.db.user", user); err != nil {
106+
m.logger.Warnf(ctx, `Failed to sync user to local state for config "%s": %s`, config.Name, err.Error())
81107
}
82-
m.logger.Debugf(ctx, `Updated configuration "%s" with workspace details`, config.Name)
83-
84-
// Update remote state with the updated config
85-
configState.SetRemoteState(config)
86-
87-
// Sync workspace details to local state if it exists
88-
if configState.Local != nil {
89-
if err := configState.Local.Content.SetNested("parameters.db.workspaceId", workspaceID); err != nil {
90-
m.logger.Warnf(ctx, `Failed to sync workspaceId to local state for config "%s": %s`, config.Name, err.Error())
91-
}
92-
normalizeWorkspaceID(configState.Local)
93-
// Also sync other workspace details
94-
if host, found, _ := config.Content.GetNested("parameters.db.host"); found {
95-
if err := configState.Local.Content.SetNested("parameters.db.host", host); err != nil {
96-
m.logger.Warnf(ctx, `Failed to sync host to local state for config "%s": %s`, config.Name, err.Error())
97-
}
98-
}
99-
if user, found, _ := config.Content.GetNested("parameters.db.user"); found {
100-
if err := configState.Local.Content.SetNested("parameters.db.user", user); err != nil {
101-
m.logger.Warnf(ctx, `Failed to sync user to local state for config "%s": %s`, config.Name, err.Error())
102-
}
103-
}
104-
if database, found, _ := config.Content.GetNested("parameters.db.database"); found {
105-
if err := configState.Local.Content.SetNested("parameters.db.database", database); err != nil {
106-
m.logger.Warnf(ctx, `Failed to sync database to local state for config "%s": %s`, config.Name, err.Error())
107-
}
108-
}
109-
scheduleLocalSave(configState)
108+
}
109+
if database, found, _ := config.Content.GetNested("parameters.db.database"); found {
110+
if err := configState.Local.Content.SetNested("parameters.db.database", database); err != nil {
111+
m.logger.Warnf(ctx, `Failed to sync database to local state for config "%s": %s`, config.Name, err.Error())
110112
}
111113
}
114+
scheduleLocalSave(configState)
112115
}
113116

114117
// Process loaded configs - backfill workspace details

internal/pkg/mapper/variables/local_persist.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ func (m *variablesMapper) AfterLocalOperation(_ context.Context, changes *model.
6464
if component.IsVariables() {
6565
configs[config.ConfigKey] = true
6666
}
67-
} else if row, ok := object.(*model.ConfigRow); ok {
67+
continue
68+
}
69+
if row, ok := object.(*model.ConfigRow); ok {
6870
// Variables values row?
6971
component, err := m.state.Components().GetOrErr(row.ComponentID)
7072
if err != nil {

internal/pkg/plan/diffop/plan.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,27 @@ func (p *Plan) Log(w io.Writer) {
5151

5252
if len(actions) == 0 {
5353
fmt.Fprintln(w, " no difference")
54-
} else {
55-
skippedDeleteCount := 0
56-
for _, action := range actions {
57-
msg := action.String()
58-
if !p.allowedRemoteDelete && action.action == ActionDeleteRemote {
59-
// determine if it is ignored or skipped
60-
if action.IsIgnored() {
61-
msg += " - IGNORED"
62-
} else {
63-
msg += " - SKIPPED"
64-
}
65-
66-
skippedDeleteCount++
54+
return
55+
}
56+
57+
skippedDeleteCount := 0
58+
for _, action := range actions {
59+
msg := action.String()
60+
if !p.allowedRemoteDelete && action.action == ActionDeleteRemote {
61+
// determine if it is ignored or skipped
62+
if action.IsIgnored() {
63+
msg += " - IGNORED"
64+
} else {
65+
msg += " - SKIPPED"
6766
}
68-
fmt.Fprintln(w, " "+msg)
69-
}
7067

71-
if skippedDeleteCount > 0 {
72-
fmt.Fprintln(w, "Skipped remote objects deletion, use \"--force\" to delete them.")
68+
skippedDeleteCount++
7369
}
70+
fmt.Fprintln(w, " "+msg)
71+
}
72+
73+
if skippedDeleteCount > 0 {
74+
fmt.Fprintln(w, "Skipped remote objects deletion, use \"--force\" to delete them.")
7475
}
7576
}
7677

internal/pkg/platform/schema/compiler/extension/primarykey/generatekey.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@ func GenerateKeys(config *gen.Config) error {
3535
// Create Go file with a key struct for each schema
3636
for _, schema := range graph.Schemas {
3737
for _, field := range schema.Fields {
38-
if field.Name == "id" {
39-
if asMap, ok := field.Annotations[pkAnnotationName]; ok {
40-
// asMap is PKAnnotation type serialized to a map
41-
pkAnnotation := PKAnnotation{}
42-
if err := json.ConvertByJSON(asMap, &pkAnnotation); err != nil {
43-
return err
44-
}
45-
if err := generateKey(targetDir, schema, pkAnnotation); err != nil {
46-
return err
47-
}
38+
if field.Name != "id" {
39+
continue
40+
}
41+
42+
if asMap, ok := field.Annotations[pkAnnotationName]; ok {
43+
// asMap is PKAnnotation type serialized to a map
44+
pkAnnotation := PKAnnotation{}
45+
if err := json.ConvertByJSON(asMap, &pkAnnotation); err != nil {
46+
return err
47+
}
48+
if err := generateKey(targetDir, schema, pkAnnotation); err != nil {
49+
return err
4850
}
4951
}
5052
}

0 commit comments

Comments
 (0)