Skip to content

Duplicate Code: URL Param Parsing Pattern Duplicated Across Tags and Statuses Modules #34426

@github-actions

Description

@github-actions

Analysis of commit 199f656

Assignee: @copilot

Summary

The parseTagsParam and parseStatusesParam functions in the manager-api modules follow an almost identical structural pattern for parsing URL parameters with ; separators and ! negation prefixes, yet are implemented separately in two different files. Both functions are approximately 25–30 lines long, yielding over 50 lines of near-duplicate parsing logic.

Duplication Details

Pattern: URL Filter Parameter Parsing (parseTagsParam / parseStatusesParam)

  • Severity: Medium
  • Occurrences: 2 instances (one per file)
  • Locations:
    • code/core/src/manager-api/modules/tags.ts (lines 18–46)
    • code/core/src/manager-api/modules/statuses.ts (lines 9–40)
  • Code Sample:
// tags.ts
export const parseTagsParam = (
  tagsParam: string | undefined
): { included: Tag[]; excluded: Tag[] } => {
  if (!tagsParam) {
    return { included: [], excluded: [] };
  }
  const included: Tag[] = [];
  const excluded: Tag[] = [];
  tagsParam.split(';').forEach((rawTag) => {
    if (!rawTag) return;
    const isExcluded = rawTag.startsWith('!');
    const normalizedTag = isExcluded ? rawTag.slice(1) : rawTag;
    const mappedTag = (BUILT_IN_URL_TAG_MAP[normalizedTag] ?? normalizedTag) as Tag;
    if (isExcluded) excluded.push(mappedTag);
    else included.push(mappedTag);
  });
  return { included, excluded };
};

// statuses.ts — structurally identical, only the value transformation differs
export const parseStatusesParam = (
  statusesParam: string | undefined
): { included: StatusValue[]; excluded: StatusValue[] } => {
  if (!statusesParam) {
    return { included: [], excluded: [] };
  }
  const included: StatusValue[] = [];
  const excluded: StatusValue[] = [];
  statusesParam.split(';').forEach((rawStatus) => {
    if (!rawStatus) return;
    const isExcluded = rawStatus.startsWith('!');
    const shortName = isExcluded ? rawStatus.slice(1) : rawStatus;
    const statusValue = toStatusValue(shortName);
    if (!statusValue) return;  // only difference: silently ignore unknown values
    if (isExcluded) excluded.push(statusValue);
    else included.push(statusValue);
  });
  return { included, excluded };
};

The algorithm is identical: split by ;, strip ! prefix for excluded items, transform the raw string to a typed value, and push into included/excluded arrays.

Impact Analysis

  • Maintainability: Any change to the URL parameter format (e.g. separator character, escaping rules) must be applied in two places, with a high risk of divergence.
  • Bug Risk: The two implementations could silently diverge over time — for example, parseTagsParam does not silently ignore unmapped values, while parseStatusesParam does. This inconsistency may cause subtle URL parameter handling bugs.
  • Code Bloat: ~50 lines of near-identical logic that could be reduced to ~20 lines with a shared generic utility.

Refactoring Recommendations

  1. Extract a generic parseFilterParam utility

    • Create: code/core/src/manager-api/lib/parseFilterParam.ts (or add to existing lib/ utilities)
    • Signature: parseFilterParam(T)(param: string | undefined, transform: (raw: string) => T | undefined): { included: T[]; excluded: T[] }
    • The transform callback handles value mapping (for tags: BUILT_IN_URL_TAG_MAP lookup; for statuses: toStatusValue() call)
    • Estimated effort: ~30 minutes
    • Benefits: Single place to fix URL param parsing bugs, consistent behavior, reduced code size
  2. Update callers

    • parseTagsParam becomes: parseFilterParam(tagsParam, (raw) => (BUILT_IN_URL_TAG_MAP[raw] ?? raw) as Tag)
    • parseStatusesParam becomes: parseFilterParam(statusesParam, toStatusValue)
    • Both callers retain their typed return signatures via generic inference

Implementation Checklist

  • Review duplication findings
  • Create parseFilterParam generic utility
  • Refactor parseTagsParam to use the utility
  • Refactor parseStatusesParam to use the utility
  • Verify no functionality broken (existing tests for both functions)
  • Update tests if needed

Analysis Metadata

Generated by Duplicate Code Detector ·

To install this agentic workflow, run

gh aw add github/gh-aw/.github/workflows/duplicate-code-detector.md@852cb06ad52958b402ed982b69957ffc57ca0619

Metadata

Metadata

Assignees

No one assigned

    Labels

    cleanupMinor cleanup style change that won't show up in release changelogtech debt

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions