Skip to content

Fix ClickUp view filter payload formatting - #31

Closed
Lovlace777 wants to merge 1 commit into
Chykalophia:mainfrom
Lovlace777:lovelace/fix-view-filter-payload
Closed

Lovlace777 wants to merge 1 commit into
Chykalophia:mainfrom
Lovlace777:lovelace/fix-view-filter-payload

Conversation

@Lovlace777

@Lovlace777 Lovlace777 commented Jun 19, 2026 •

Copy link
Copy Markdown

Summary

  • format view filters with the ClickUp API field names op and values
  • map supported filter operators to their API values and preserve falsy scalar values
  • encode set/unset filters with the documented [null] values payload
  • add regression coverage for both view creation and filter updates

Fixes #29

Validation

  • npx jest --config packages/core/jest.config.js --runInBand --coverage=false - passed 11 suites and 180 tests
  • npm run build --workspace=packages/core - passed
  • npx eslint packages/core/src/clickup-client/views-enhanced.ts packages/core/src/tests/views-filter-payload.test.ts - passed with one pre-existing max-line-length warning on views-enhanced.ts:2
  • git diff --check - passed
  • root npm ci reached the workspace prepare build and failed on existing TypeScript errors in packages/intelligence; core dependencies were installed separately for the checks above, without modifying the lockfile

Notes

  • no dependencies or lockfiles changed
  • the change only affects outgoing view filter payload formatting
  • existing callers can continue using the current schema operator names and scalar or array values

Summary by cubic

Fixes ClickUp view filter payloads to match the API and prevent rejected requests. We now send op and values, map operators, preserve falsy scalars, and encode set/unset as [null].

  • Bug Fixes
    • Use API field keys (op, values) and map operators: EQ, NOT, GT, LT, GTE, LTE, ANY, NOT ANY, IS SET, IS NOT SET.
    • Normalize values: always send a values array; preserve falsy scalars; support arrays; encode set/unset as [null].
    • Add regression tests for view creation and filter updates.

Written for commit 3867dcd. Summary will update on new commits.

Review in cubic

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds CLICKUP_FILTER_OPERATOR_MAP to translate FilterOperator enum values to ClickUp API operator strings. Updates formatFilters to use this map for the op field and introduces formatFilterValues to normalize filter inputs (values, value, is_set/is_not_set) into a consistent values array. Adds a Jest test suite validating the resulting payloads for createView and setViewFilters.

Changes

Filter Operator Mapping and Payload Normalization

Layer / File(s) Summary
Operator map and filter value normalization
packages/core/src/clickup-client/views-enhanced.ts
Adds CLICKUP_FILTER_OPERATOR_MAP mapping FilterOperator values to ClickUp strings (EQ, ANY, NOT ANY, IS SET). Updates formatFilters to look up op from the map (falling back to the raw operator), and introduces/expands formatFilterValues to normalize filter.values, scalar filter.value, array filter.value, and is_set/is_not_set (mapped to [null]) into a unified values array.
Filter payload tests
packages/core/src/tests/views-filter-payload.test.ts
Introduces TestViewsClient test double that records post/put calls. Adds tests asserting createView produces the correct filters.op, filters.fields with normalized op/values, default show_closed, and absence of per-field operator/value keys. Also tests setViewFilters emits the correct mapped operator and [null] for is_not_set.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing ClickUp view filter payload formatting to use correct API field names.
Description check ✅ Passed The description is directly related to the changeset, covering the bug fix, implementation approach, validation steps, and noting that no dependencies were modified.
Linked Issues check ✅ Passed The PR fully addresses issue #29 by fixing the operator to op field mapping, implementing operator normalization, and handling filter value encoding as specified.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing view filter payload formatting and adding regression tests; no out-of-scope modifications to dependencies, lockfiles, or unrelated functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/core/src/tests/views-filter-payload.test.ts (1)

64-79: ⚡ Quick win

Add a direct updateView filter payload regression test.

The bug scope includes updateView as a caller of formatFilters, but this suite currently validates only createView and setViewFilters. Add one explicit updateView assertion so all affected entry points are locked.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/tests/views-filter-payload.test.ts` around lines 64 - 79,
The test suite is missing explicit coverage for updateView, which is also a
caller of formatFilters that needs validation. Add a new test case in this file
that calls client.updateView with filters (similar to the existing
setViewFilters test shown in the diff), and assert that the filter payload is
correctly transformed with proper operator names (like NOT ANY and IS NOT SET)
and values in the putCalls data. This ensures updateView is locked into the
regression test coverage alongside createView and setViewFilters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/clickup-client/views-enhanced.ts`:
- Around line 19-30: Change the CLICKUP_FILTER_OPERATOR_MAP type from
Partial<Record<FilterOperator, string>> to a total Record<FilterOperator,
string> to require all FilterOperator values to be mapped. Additionally, locate
where this map is used with a fallback operator (such as the `??
filter.operator` pattern mentioned around lines 334-335) and remove the fallback
entirely. This will ensure all FilterOperator cases are handled at compile time
rather than risking runtime 400 errors from unmapped operators being sent in
payloads.
- Around line 342-355: The formatFilterValues method currently checks for
filter.values and array filter.value before validating the operator type,
allowing is_set and is_not_set operators to emit non-null values when callers
provide them, which contradicts the intended ClickUp encoding. Reorder the
checks in formatFilterValues so that the operator validation for is_set and
is_not_set comes first, before any checks for filter.values or filter.value,
ensuring these operators always return [null] regardless of input values
provided.

---

Nitpick comments:
In `@packages/core/src/tests/views-filter-payload.test.ts`:
- Around line 64-79: The test suite is missing explicit coverage for updateView,
which is also a caller of formatFilters that needs validation. Add a new test
case in this file that calls client.updateView with filters (similar to the
existing setViewFilters test shown in the diff), and assert that the filter
payload is correctly transformed with proper operator names (like NOT ANY and IS
NOT SET) and values in the putCalls data. This ensures updateView is locked into
the regression test coverage alongside createView and setViewFilters.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5849a5fd-5ca6-45d6-ab5f-18e7c00ae5c7

📥 Commits

Reviewing files that changed from the base of the PR and between 26f0db5 and 3867dcd.

📒 Files selected for processing (2)
  • packages/core/src/clickup-client/views-enhanced.ts
  • packages/core/src/tests/views-filter-payload.test.ts

Comment on lines +19 to +30
const CLICKUP_FILTER_OPERATOR_MAP: Partial<Record<FilterOperator, string>> = {
equals: 'EQ',
not_equals: 'NOT',
greater_than: 'GT',
less_than: 'LT',
greater_than_or_equal: 'GTE',
less_than_or_equal: 'LTE',
in: 'ANY',
not_in: 'NOT ANY',
is_set: 'IS SET',
is_not_set: 'IS NOT SET'
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make operator mapping exhaustive and remove silent fallback.

Using Partial<Record<...>> with ?? filter.operator can emit raw schema operators for unmapped cases, which risks reintroducing 400s for valid FilterOperator inputs. Make this a total Record<FilterOperator, string> and drop the fallback so unmapped operators fail at compile time instead of runtime payload errors.

Suggested diff
-const CLICKUP_FILTER_OPERATOR_MAP: Partial<Record<FilterOperator, string>> = {
+const CLICKUP_FILTER_OPERATOR_MAP: Record<FilterOperator, string> = {
   equals: 'EQ',
   not_equals: 'NOT',
+  contains: 'CONTAINS',
+  not_contains: 'NOT CONTAINS',
+  starts_with: 'STARTS WITH',
+  ends_with: 'ENDS WITH',
+  is_empty: 'IS EMPTY',
+  is_not_empty: 'IS NOT EMPTY',
   greater_than: 'GT',
   less_than: 'LT',
   greater_than_or_equal: 'GTE',
   less_than_or_equal: 'LTE',
+  between: 'BETWEEN',
+  not_between: 'NOT BETWEEN',
   in: 'ANY',
   not_in: 'NOT ANY',
   is_set: 'IS SET',
   is_not_set: 'IS NOT SET'
 };
...
-        op: CLICKUP_FILTER_OPERATOR_MAP[filter.operator] ?? filter.operator,
+        op: CLICKUP_FILTER_OPERATOR_MAP[filter.operator],

Also applies to: 334-335

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/clickup-client/views-enhanced.ts` around lines 19 - 30,
Change the CLICKUP_FILTER_OPERATOR_MAP type from Partial<Record<FilterOperator,
string>> to a total Record<FilterOperator, string> to require all FilterOperator
values to be mapped. Additionally, locate where this map is used with a fallback
operator (such as the `?? filter.operator` pattern mentioned around lines
334-335) and remove the fallback entirely. This will ensure all FilterOperator
cases are handled at compile time rather than risking runtime 400 errors from
unmapped operators being sent in payloads.

Comment on lines +342 to +355
private formatFilterValues(filter: ViewFilter): Array<string | number | null> {
if (filter.values !== undefined) {
return filter.values;
}

if (Array.isArray(filter.value)) {
return filter.value;
}

if (filter.operator === 'is_set' || filter.operator === 'is_not_set') {
return [null];
}

return filter.value !== undefined ? [filter.value] : [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Force [null] for is_set/is_not_set before reading input values.

formatFilterValues currently gives precedence to filter.values/array filter.value, so is_set/is_not_set can emit non-[null] payloads when callers provide values. This contradicts the intended ClickUp encoding and can produce invalid requests.

Suggested diff
  private formatFilterValues(filter: ViewFilter): Array<string | number | null> {
+    if (filter.operator === 'is_set' || filter.operator === 'is_not_set') {
+      return [null];
+    }
+
    if (filter.values !== undefined) {
      return filter.values;
    }

    if (Array.isArray(filter.value)) {
      return filter.value;
    }

-    if (filter.operator === 'is_set' || filter.operator === 'is_not_set') {
-      return [null];
-    }
-
    return filter.value !== undefined ? [filter.value] : [];
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/clickup-client/views-enhanced.ts` around lines 342 - 355,
The formatFilterValues method currently checks for filter.values and array
filter.value before validating the operator type, allowing is_set and is_not_set
operators to emit non-null values when callers provide them, which contradicts
the intended ClickUp encoding. Reorder the checks in formatFilterValues so that
the operator validation for is_set and is_not_set comes first, before any checks
for filter.values or filter.value, ensuring these operators always return [null]
regardless of input values provided.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/core/src/clickup-client/views-enhanced.ts">

<violation number="1" location="packages/core/src/clickup-client/views-enhanced.ts:19">
P1: Operator map is non-exhaustive and silently falls back to raw schema names for unmapped operators, risking silent API failures.</violation>

<violation number="2" location="packages/core/src/clickup-client/views-enhanced.ts:343">
P2: Set/unset operators do not always force `[null]` because earlier branches return caller-supplied `values`/array `value`</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// ViewSettings
} from '../schemas/views-schemas.js';

const CLICKUP_FILTER_OPERATOR_MAP: Partial<Record<FilterOperator, string>> = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Operator map is non-exhaustive and silently falls back to raw schema names for unmapped operators, risking silent API failures.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/src/clickup-client/views-enhanced.ts, line 19:

<comment>Operator map is non-exhaustive and silently falls back to raw schema names for unmapped operators, risking silent API failures.</comment>

<file context>
@@ -10,11 +10,25 @@ import type {
   // ViewSettings
 } from '../schemas/views-schemas.js';
 
+const CLICKUP_FILTER_OPERATOR_MAP: Partial<Record<FilterOperator, string>> = {
+  equals: 'EQ',
+  not_equals: 'NOT',
</file context>

}

private formatFilterValues(filter: ViewFilter): Array<string | number | null> {
if (filter.values !== undefined) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Set/unset operators do not always force [null] because earlier branches return caller-supplied values/array value

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/src/clickup-client/views-enhanced.ts, line 343:

<comment>Set/unset operators do not always force `[null]` because earlier branches return caller-supplied `values`/array `value`</comment>

<file context>
@@ -317,14 +331,30 @@ export class ViewsEnhancedClient extends ClickUpClient {
   }
 
+  private formatFilterValues(filter: ViewFilter): Array<string | number | null> {
+    if (filter.values !== undefined) {
+      return filter.values;
+    }
</file context>

Copy link
Copy Markdown
Contributor

Thanks for this! Same as #30 — the view-filter operator→op payload bug (issue #29) is fully resolved by the broader API overhaul in #37, going out via the dev branch for v6.0.0. There the views client uses op throughout (input schema, ViewFilter type, and the formatFilters() payload builder). Closing as superseded; the fix is shipping in v6.0.0. 🙏


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: createView filter fields use operator key but ClickUp API requires op

2 participants