Skip to content

feat(client): align filtering with API usage#26

Merged
angelxmoreno merged 1 commit intomainfrom
fix/filtering-alignment
Dec 21, 2025
Merged

feat(client): align filtering with API usage#26
angelxmoreno merged 1 commit intomainfrom
fix/filtering-alignment

Conversation

@angelxmoreno
Copy link
Copy Markdown
Owner

@angelxmoreno angelxmoreno commented Dec 21, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for multiple filters in a single API request, with per-field query parameters and operator syntax.
  • Documentation

    • Updated API usage examples to reflect new filter parameter structure with direct query parameters.
  • Changes

    • The estimatedMarketValue field is no longer available as a filterable option.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 21, 2025

Walkthrough

This PR implements multi-field filtering capabilities by replacing a single-filter model with a per-field query parameter approach. It introduces a two-step filter pipeline (normalize and apply), updates test suites to validate the new behavior, revises documentation, adds SQLite cache dependencies, and removes estimatedMarketValue from filterable fields.

Changes

Cohort / File(s) Summary
Configuration and dependencies
.gitignore, package.json
Added testCache.sqlite to git ignore; added @keyvhq/sqlite@^2.1.11 as a dev dependency.
Documentation
README.md
Updated examples to demonstrate per-field query parameters (e.g., releaseYear: '2025') and the filters helper; revised notes explaining URL-level query parameters with field+operator syntax.
Type definitions
src/interfaces.ts
Removed estimatedMarketValue from FILTERABLE_FIELDS; updated PaginatedOptions.filters to accept either a single FilterOption or an array of FilterOption.
Core client logic
src/TheSneakerDatabaseClient.ts
Replaced single-filter handling with a two-step pipeline: introduced normalizeFilters() and applyFilterParams() methods for multi-filter validation and application; added serializeFilterValueWithOperator() helper; removed ensureSingleFilter() and serializeFilter() methods; added validation for field presence, duplicate filters, value presence, and operator support.
E2E tests
src/TheSneakerDatabaseClient.e2e.test.ts
Added SQLite-backed cache setup; replaced test fixtures with per-field filter validation; added parameterized filter tests for releaseDate and retailPrice; added multi-filter combination test; updated search and sort test data (NIKE instead of Jordan); removed API-level rejection tests.
Integration tests
src/TheSneakerDatabaseClient.integration.test.ts
Updated mock expectations to validate per-field query parameters instead of a single filters param; added tests for multiple filters, duplicate field filters, and conflicts between filters and explicit params; increased default limit values from 5 to 100; removed estimatedMarketValue from filter fixtures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

  • Filter validation pipeline: Review the new normalizeFilters() and applyFilterParams() logic for correctness in handling duplicates, conflicts, and field validation
  • Type changes: Verify that FilterOption[] union type (single or array) is properly handled throughout and doesn't introduce type-safety regressions
  • Test coverage: Ensure integration and e2e tests comprehensively validate the per-field query parameter serialization, operator handling, and multi-filter scenarios
  • Operator serialization: Examine serializeFilterValueWithOperator() for edge cases and consistency with expected URL formatting

Possibly related PRs

  • 20 support rich filtering sorting options #21: Both PRs modify the same filtering/sorting feature (interfaces, query-param serialization, and tests), evolving the single-filter approach into per-field/multi-filter handling.
  • Feat/e2e tests #7: This PR builds on and updates the e2e test file introduced by PR #7, modifying client behavior used by those tests.

Poem

🐰 Multi-filters dance in query strings so bright,
Per-field parameters gleam in URL light,
No more single constraints on our SQLite cache,
The sneaker database runs at a faster pace!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the primary objective of aligning the client's filtering implementation with API usage patterns, which is the central theme across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/filtering-alignment

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.47%. Comparing base (4b589f6) to head (e39a526).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #26      +/-   ##
==========================================
+ Coverage   99.42%   99.47%   +0.05%     
==========================================
  Files           3        3              
  Lines         174      192      +18     
==========================================
+ Hits          173      191      +18     
  Misses          1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/TheSneakerDatabaseClient.e2e.test.ts (2)

48-76: Clarify why releaseYear tests are skipped.

The describe.skip lacks a comment explaining why these tests are disabled. If this is due to API limitations or pending backend support, adding a brief comment would help future maintainers understand when to enable them.

-        describe.skip('using releaseYear', () => {
+        // TODO: Enable when releaseYear filtering is supported by the API
+        describe.skip('using releaseYear', () => {

158-161: Consider a soft assertion instead of early return.

Returning early when results are empty means no assertion runs. If the API consistently returns empty results, this test would silently pass. Consider using a soft warning with expect to track this:

                if (payload.results.length === 0) {
                    console.warn('Combined filter request returned 0 results – RapidAPI may not have matching data.');
-                    return;
+                    // Still pass but log warning - flaky test detection will surface patterns
+                    expect(payload.results.length).toBeGreaterThanOrEqual(0);
+                    return; // Skip detailed assertions
                }

Alternatively, you could track this as a known-flaky test if the API data is inconsistent.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97458ed and e39a526.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .gitignore (1 hunks)
  • README.md (1 hunks)
  • package.json (1 hunks)
  • src/TheSneakerDatabaseClient.e2e.test.ts (5 hunks)
  • src/TheSneakerDatabaseClient.integration.test.ts (9 hunks)
  • src/TheSneakerDatabaseClient.ts (2 hunks)
  • src/interfaces.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Source TypeScript files should be organized in src/ directory with TheSneakerDatabaseClient.ts exposing the SDK, interfaces.ts for shared types, utils.ts for helpers, and index.ts re-exporting public modules
Use Prettier-equivalent formatting with 4-space indent, single quotes, trailing commas, semicolons, and 120-character line wraps in TypeScript files
Use PascalCase for class names in TypeScript
Use camelCase for function names in TypeScript
Use UPPER_SNAKE_CASE for exported constants in TypeScript
Validate payloads with the interfaces module before sending requests in TypeScript client code

Files:

  • src/TheSneakerDatabaseClient.ts
  • src/TheSneakerDatabaseClient.e2e.test.ts
  • src/interfaces.ts
  • src/TheSneakerDatabaseClient.integration.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Test files should use the *.test.ts naming convention and be colocated with their target implementation files
Add unit test specs alongside code using *.test.ts with Bun's built-in test runner and axios-mock-adapter for offline testing

Files:

  • src/TheSneakerDatabaseClient.e2e.test.ts
  • src/TheSneakerDatabaseClient.integration.test.ts
🧠 Learnings (2)
📚 Learning: 2025-12-17T08:35:10.837Z
Learnt from: CR
Repo: angelxmoreno/sneakerdb-client PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T08:35:10.837Z
Learning: Applies to src/**/*.ts : Source TypeScript files should be organized in `src/` directory with `TheSneakerDatabaseClient.ts` exposing the SDK, `interfaces.ts` for shared types, `utils.ts` for helpers, and `index.ts` re-exporting public modules

Applied to files:

  • src/TheSneakerDatabaseClient.ts
  • src/TheSneakerDatabaseClient.e2e.test.ts
📚 Learning: 2025-12-17T08:35:10.837Z
Learnt from: CR
Repo: angelxmoreno/sneakerdb-client PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T08:35:10.837Z
Learning: Applies to **/*.test.ts : Add unit test specs alongside code using `*.test.ts` with Bun's built-in test runner and `axios-mock-adapter` for offline testing

Applied to files:

  • src/TheSneakerDatabaseClient.e2e.test.ts
  • src/TheSneakerDatabaseClient.integration.test.ts
🧬 Code graph analysis (3)
src/TheSneakerDatabaseClient.ts (1)
src/interfaces.ts (4)
  • FilterOption (49-53)
  • ComparableField (45-45)
  • FILTERABLE_FIELDS (43-43)
  • FilterOperator (41-41)
src/TheSneakerDatabaseClient.e2e.test.ts (3)
src/interfaces.ts (2)
  • FilterOption (49-53)
  • GetSneakersResponse (83-86)
src/TheSneakerDatabaseClient.ts (1)
  • TheSneakerDatabaseClient (29-272)
src/index.ts (2)
  • TheSneakerDatabaseClient (14-14)
  • GetSneakersResponse (4-4)
src/TheSneakerDatabaseClient.integration.test.ts (1)
src/interfaces.ts (4)
  • GetSneakersOptions (71-81)
  • GetSneakersResponse (83-86)
  • FILTERABLE_FIELDS (43-43)
  • FilterOption (49-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: tests
🔇 Additional comments (20)
.gitignore (1)

131-132: LGTM!

Adding testCache.sqlite to .gitignore is appropriate to prevent test-generated SQLite cache files from being committed. This aligns with the new @keyvhq/sqlite devDependency.

package.json (1)

64-64: LGTM!

The @keyvhq/sqlite devDependency version ^2.1.11 correctly matches the @keyvhq/core version, ensuring compatibility between the cache packages used in tests.

README.md (1)

39-51: LGTM!

The documentation updates accurately reflect the new per-field query parameter approach. The example demonstrates both direct parameters (releaseYear: '2025') and the filters helper, with a clear explanation of how the API expects these parameters at the URL level.

src/interfaces.ts (2)

43-43: LGTM - estimatedMarketValue removed from filterable fields.

The FILTERABLE_FIELDS constant now correctly includes only releaseDate, releaseYear, and retailPrice, aligning with the API's actual filtering capabilities.

If this is a breaking change for existing consumers who were filtering by estimatedMarketValue, consider documenting it in the changelog or release notes.


68-68: LGTM!

The union type FilterOption | FilterOption[] provides flexibility for both single-filter and multi-filter use cases, aligning well with the new per-field filtering approach.

src/TheSneakerDatabaseClient.integration.test.ts (6)

58-62: LGTM!

The filterFixtures record is type-safe, using FilterOption['field'] as the key type to ensure alignment with FILTERABLE_FIELDS. This keeps test fixtures in sync with the interface definition.


252-288: LGTM!

The filter serialization tests thoroughly validate the per-field query parameter approach:

  • Verifies filters are sent as individual field params (e.g., releaseYear: 'gte:2020')
  • Confirms the filters property is not sent in the request
  • Tests the eq operator optimization (value without operator prefix)
  • Dynamic iteration over FILTERABLE_FIELDS ensures comprehensive coverage

290-302: LGTM!

The test correctly validates that omitting the operator defaults to eq behavior, serializing the value as a plain string ('200') without an operator prefix.


304-325: LGTM!

This test validates the core multi-field filtering capability, ensuring that multiple filters on distinct fields are correctly serialized as separate query parameters (releaseYear: 'gte:2000', retailPrice: 'lte:400').


327-344: LGTM!

This test validates important duplicate filter detection logic. Attempting to apply multiple filters to the same field (e.g., two releaseYear constraints) correctly fails validation before making any network request, with a clear error message.


346-361: LGTM!

This test validates the conflict detection between explicit query parameters and the filters option. When both releaseYear: '2025' and filters: { field: 'releaseYear', ... } are provided, the client correctly rejects the request with a descriptive error before making any network call.

src/TheSneakerDatabaseClient.ts (4)

148-155: LGTM on the filter integration into query params.

The integration of applyFilterParams into prepareQueryParams is clean. The method correctly handles the case where no valid filters exist by returning false and deleting the filters key.


170-188: Conflict detection logic is sound.

The applyFilterParams method correctly:

  1. Normalizes filters via normalizeFilters
  2. Detects conflicts when a filter field already exists as a query parameter
  3. Serializes each filter to its field key and removes the original filters key

One consideration: the deletion of normalized.filters at line 186 is always executed when filters exist, which is the correct behavior since filters are expanded into per-field parameters.


190-234: Thorough validation in normalizeFilters.

The method handles:

  • Undefined input gracefully (returns empty array)
  • Both single filter objects and arrays
  • Field presence and validity checks
  • Duplicate field detection via seenFields Set
  • Value presence validation
  • Operator validation against allowed set

The error messages are descriptive and actionable.


236-240: Clean operator serialization with sensible default.

The serializeFilterValueWithOperator method correctly defaults to eq and only prefixes the operator when it's not eq, which aligns with typical API query parameter conventions.

src/TheSneakerDatabaseClient.e2e.test.ts (5)

14-14: Type extension is well-designed.

FilterOptionWithCompare cleanly extends FilterOption with a compare function for runtime validation of filter results. This enables data-driven testing with per-field comparison logic.


107-145: Well-structured parameterized filter tests.

The test design is solid:

  • Each filterable field has its own compare function for runtime validation
  • Tests verify every returned sneaker satisfies the filter condition
  • Diagnostic logging helps debug API discrepancies

Minor observation: The biome-ignore comment at line 131-132 is acceptable given the dynamic field access pattern.


147-169: Good coverage for combined filters with graceful handling of empty results.

The test correctly:

  1. Applies multiple filters simultaneously
  2. Handles zero-result scenarios with a warning rather than failure (appropriate for e2e tests against live APIs)
  3. Validates each result against all filter criteria

33-34: Empty describe block when e2e is disabled.

The empty describe('TheSneakerDatabaseClient:e2e', () => {}); is a valid pattern to avoid test runner complaints about missing tests. This is cleaner than the previous describe.skip approach.


38-42: SQLite cache setup is appropriate for persistent e2e test caching.

Using @keyvhq/sqlite with a 24-hour TTL is reasonable for e2e tests to reduce API calls across test runs. The namespace testv1 helps with cache versioning.

The test cache file testCache.sqlite is already in .gitignore, preventing accidental commits of test cache files.

@angelxmoreno angelxmoreno merged commit 7514a03 into main Dec 21, 2025
4 checks passed
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.

1 participant