Skip to content

refactor: extract shared project config parsing helpers to eliminate duplication#20939

Merged
pelikhan merged 5 commits intomainfrom
copilot/refactor-duplicate-project-config-parsing
Mar 14, 2026
Merged

refactor: extract shared project config parsing helpers to eliminate duplication#20939
pelikhan merged 5 commits intomainfrom
copilot/refactor-duplicate-project-config-parsing

Conversation

Copy link
Contributor

Copilot AI commented Mar 14, 2026

parseCreateProjectsConfig and parseUpdateProjectConfig contained ~100 lines of identical views and field-definitions parsing logic, creating a divergence risk where fixes or schema changes had to be applied twice.

Changes

  • pkg/workflow/project_config_parsing.go (new): two standalone helpers:

    • parseProjectViews(configMap map[string]any, log *logger.Logger) []ProjectView
    • parseProjectFieldDefinitions(configMap map[string]any, log *logger.Logger) []ProjectFieldDefinition
  • create_project.go / update_project.go: inline parsing blocks replaced with calls to the shared helpers; per-file loggers are passed through so debug output remains contextual.

  • pkg/workflow/project_config_parsing_test.go (new): table-driven tests for each helper (missing keys, invalid items, all optional fields) plus a parity test asserting both command paths produce identical results for the same input.

// Before: ~50 lines of inline loops in each of create_project.go and update_project.go
// After:
createProjectsConfig.Views = parseProjectViews(configMap, createProjectLog)
createProjectsConfig.FieldDefinitions = parseProjectFieldDefinitions(configMap, createProjectLog)

Copilot AI and others added 2 commits March 14, 2026 04:01
…duplication

Extract parseProjectViews and parseProjectFieldDefinitions from the duplicated
blocks in parseCreateProjectsConfig and parseUpdateProjectConfig into a shared
pkg/workflow/project_config_parsing.go file.

Both methods now delegate to the helpers, eliminating ~100 lines of duplicated
code and ensuring a single source of truth for views/field-definitions parsing.

Also adds parity tests in project_config_parsing_test.go covering the helpers
directly and verifying identical behavior from both the create and update paths.

Fixes #<issue>"

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 14, 2026 14:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors duplicated views and field-definitions parsing logic out of the create/update project config paths into shared helpers to reduce drift and centralize behavior.

Changes:

  • Added shared parsing helpers for project views and field-definitions.
  • Updated create-project and update-project config parsing to call the shared helpers.
  • Added new unit tests covering the helpers plus a parity check across both command paths.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pkg/workflow/project_config_parsing.go New shared parsing helpers for views and field-definitions.
pkg/workflow/create_project.go Replaces inline parsing with calls to shared helpers.
pkg/workflow/update_project.go Replaces inline parsing with calls to shared helpers.
pkg/workflow/project_config_parsing_test.go New table-driven tests for helper behavior and create/update parity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +18 to +136
func TestParseProjectViews(t *testing.T) {
tests := []struct {
name string
input map[string]any
expected []ProjectView
}{
{
name: "no views key",
input: map[string]any{},
expected: nil,
},
{
name: "empty views list",
input: map[string]any{
"views": []any{},
},
expected: nil,
},
{
name: "single valid view with required fields only",
input: map[string]any{
"views": []any{
map[string]any{
"name": "Board",
"layout": "board",
},
},
},
expected: []ProjectView{
{Name: "Board", Layout: "board"},
},
},
{
name: "view with all optional fields",
input: map[string]any{
"views": []any{
map[string]any{
"name": "Task Board",
"layout": "board",
"filter": "is:issue",
"visible-fields": []any{1, 2, 3},
"description": "Main board",
},
},
},
expected: []ProjectView{
{
Name: "Task Board",
Layout: "board",
Filter: "is:issue",
VisibleFields: []int{1, 2, 3},
Description: "Main board",
},
},
},
{
name: "view missing name is skipped",
input: map[string]any{
"views": []any{
map[string]any{"layout": "board"},
},
},
expected: nil,
},
{
name: "view missing layout is skipped",
input: map[string]any{
"views": []any{
map[string]any{"name": "Board"},
},
},
expected: nil,
},
{
name: "mix of valid and invalid views",
input: map[string]any{
"views": []any{
map[string]any{
"name": "Valid",
"layout": "table",
},
map[string]any{
// Missing layout
"name": "Invalid",
},
map[string]any{
"name": "Also Valid",
"layout": "roadmap",
},
},
},
expected: []ProjectView{
{Name: "Valid", Layout: "table"},
{Name: "Also Valid", Layout: "roadmap"},
},
},
{
name: "non-map view item is skipped",
input: map[string]any{
"views": []any{
"not-a-map",
map[string]any{
"name": "Good",
"layout": "board",
},
},
},
expected: []ProjectView{
{Name: "Good", Layout: "board"},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := parseProjectViews(tt.input, testLog)
assert.Equal(t, tt.expected, result, "parseProjectViews result should match expected")
})
}
Comment on lines +24 to +35
{
name: "no views key",
input: map[string]any{},
expected: nil,
},
{
name: "empty views list",
input: map[string]any{
"views": []any{},
},
expected: nil,
},
@pelikhan
Copy link
Contributor

@copilot review comments

- Add wrong container type cases for views and field-definitions
  (string/map instead of []any) to lock in nil-early-return behavior
- Add mixed-type visible-fields and options cases to document that
  non-int/non-string elements are silently skipped
- Use assert.Empty for nil-expected cases to avoid brittle
  nil-vs-empty-slice comparisons

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 14, 2026

@copilot review comments

Addressed both review comments in c398e7c:

  1. Added wrong-container-type test cases ("views": "not-a-list", "views": map[string]any{...}, and equivalent for field-definitions) to lock in the early-return behavior.
  2. Added mixed-type element cases: visible-fields with non-int values and options with non-string values — both document that invalid elements are silently skipped.
  3. Replaced assert.Equal(t, nil, result, ...) with assert.Empty(t, result, ...) for all nil-result cases to avoid brittle nil-vs-empty-slice comparisons.

Copilot AI requested a review from pelikhan March 14, 2026 14:32
@pelikhan pelikhan merged commit 052be9d into main Mar 14, 2026
52 checks passed
@pelikhan pelikhan deleted the copilot/refactor-duplicate-project-config-parsing branch March 14, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate Code: project config parsing duplicated in create/update project handlers

3 participants