Skip to content

refactor: standardize config/client creation pattern in cfl #17

@rianjs

Description

@rianjs

Summary

Refactor cfl to use the Options.APIClient() pattern that jtk uses, eliminating repeated config loading code.

Current State

The same 15-line config/client creation block is copy-pasted in 8+ locations:

  • tools/cfl/internal/cmd/page/list.go (lines 94-110)
  • tools/cfl/internal/cmd/page/create.go (lines 113-130)
  • tools/cfl/internal/cmd/page/view.go (lines 81-93)
  • tools/cfl/internal/cmd/page/delete.go (lines 53-64)
  • tools/cfl/internal/cmd/search/search.go (lines 133-150)
  • tools/cfl/internal/cmd/space/list.go (lines 76-88)
  • And more...
// Current pattern (repeated everywhere)
if client == nil {
    cfg, err := config.LoadWithEnv(config.DefaultConfigPath())
    if err != nil {
        return fmt.Errorf("failed to load config: %w (run 'cfl init' to configure)", err)
    }
    if err := cfg.Validate(); err != nil {
        return fmt.Errorf("invalid config: %w (run 'cfl init' to configure)", err)
    }
    client = api.NewClient(cfg.URL, cfg.Email, cfg.APIToken)
}

Proposed Changes

Follow jtk's pattern from tools/jtk/internal/cmd/root/root.go:

func (o *Options) APIClient() (*api.Client, error) {
    if o.testClient != nil {
        return o.testClient, nil
    }
    return api.New(api.ClientConfig{...})
}
  1. Create Options struct in cfl's root command
  2. Add APIClient() method with lazy initialization
  3. Pass *Options to all subcommands via Register(parent, opts) pattern
  4. Remove duplicate config loading from each command

Impact

~100 lines removed, 8 files changed

Additional Benefit

This also improves testability - tests can inject mock clients via testClient field.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions