Skip to content

fix(deployment): require state filter for offset-based pagination#3038

Open
ygrishajev wants to merge 1 commit intomainfrom
fix/deployment-require-state-for-offset-pagination
Open

fix(deployment): require state filter for offset-based pagination#3038
ygrishajev wants to merge 1 commit intomainfrom
fix/deployment-require-state-for-offset-pagination

Conversation

@ygrishajev
Copy link
Copy Markdown
Contributor

@ygrishajev ygrishajev commented Apr 2, 2026

Why

The Akash blockchain endpoint /akash/deployment/v1beta4/deployments/list rejects requests that use pagination.offset without filters.state, returning a 400 error:

invalid request parameters. if offset is set, filter.state must be provided

The listWithResources route (/v1/addresses/:address/deployments/:skip/:limit) always provides skip (offset) as a path param but status was an optional query param, so requests without status were failing.

What

BREAKING CHANGE: The status query parameter is now required on GET /v1/addresses/{address}/deployments/{skip}/{limit}. Requests without ?status=active or ?status=closed will return 400.

  • Made status required in the ListWithResourcesQuerySchema (OpenAPI)
  • Made status required in DeploymentReaderService.listWithResources signature, removing the silent "active" default
  • Added FindAllParams union type in packages/http-sdk that enforces state is required when pagination.offset is set — catches violations at compile time
  • Updated list method to branch on skip for type narrowing
  • Updated stats-web AddressDeployments to default the status filter to "active" instead of "*" so it always sends a valid status
  • Updated empty state message to say "no active deployments"
  • Added unit tests for DeploymentHttpService.findAll and DeploymentReaderService.listWithResources
  • Added functional test for missing status returning 400

Summary by CodeRabbit

  • Tests

    • Added unit and functional tests covering deployment list filtering, required status handling, and pagination (offset vs key/absent) to ensure correct outbound queries.
  • Refactor

    • Pagination now omits offset unless provided; offset-only and non-offset modes handled distinctly.
    • Status is now required/treated as "active" by default in relevant list requests and UI, and empty-state text now shows "no active deployments."

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Refactors deployment listing to use a new exported FindAllParams, changes pagination construction to include offset only when skip is provided, makes status required for listWithResources, and adds/updates unit and functional tests plus a dev dependency for test utilities.

Changes

Cohort / File(s) Summary
Deployment Reader Service (impl & tests)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts, apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts
Switched internal calls to accept FindAllParams; pagination now omits offset when skip is undefined; listWithResources requires status and always maps it to state; added tests asserting owner, state, and pagination.offset behavior.
Deployment HTTP Service (type, impl & tests)
packages/http-sdk/src/deployment/deployment-http.service.ts, packages/http-sdk/src/deployment/deployment-http.service.spec.ts
Added exported FindAllParams union to enforce state when offset pagination is used; findAll now accepts FindAllParams; tests added to validate query/pagination construction (offset vs key) and conditional inclusion of filters.state.
Functional tests (HTTP stubs)
apps/api/test/functional/addresses.spec.ts, apps/api/test/functional/deployments.spec.ts
Updated functional tests and Nock stubs to require filters.state=active on mocked Akash deployments list requests; added a test asserting 400 when status query is missing.
HTTP SDK devDependencies
packages/http-sdk/package.json
Added vitest-mock-extended (^3.1.0) to devDependencies to support new tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hop through types and tweak the view,
Offset hides unless skip says true,
Status stands firm, no longer shy,
Tests twitch whiskers as queries fly,
A tiny rabbit, code anew.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: making the state filter required for offset-based pagination in the deployment service.
Description check ✅ Passed The description comprehensively covers the 'Why' and 'What' sections with detailed explanation of the breaking change, implementation details, and testing approach.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deployment-require-state-for-offset-pagination

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

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 59.63%. Comparing base (59a50eb) to head (392fca2).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3038      +/-   ##
==========================================
- Coverage   59.63%   59.63%   -0.01%     
==========================================
  Files        1034     1034              
  Lines       24248    24252       +4     
  Branches     6009     6012       +3     
==========================================
+ Hits        14461    14462       +1     
- Misses       8536     8539       +3     
  Partials     1251     1251              
Flag Coverage Δ
api 81.25% <100.00%> (-0.02%) ⬇️
deploy-web 43.24% <ø> (-0.01%) ⬇️
log-collector 84.92% <ø> (ø)
notifications 86.06% <ø> (ø)
provider-console 81.48% <ø> (ø)
provider-proxy 85.21% <ø> (ø)
tx-signer 76.26% <ø> (ø)
Files with missing lines Coverage Δ
...i/src/deployment/http-schemas/deployment.schema.ts 100.00% <ø> (ø)
...ces/deployment-reader/deployment-reader.service.ts 85.31% <100.00%> (+0.42%) ⬆️

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts (1)

141-142: ⚠️ Potential issue | 🟡 Minor

Avoid using any type cast.

Line 141 casts to any which violates the coding guidelines.

🛠️ Proposed fix
       return new AxiosError("HTTP Error", undefined, undefined, undefined, {
         status,
         data: {},
         statusText: "Error",
         headers: {},
-        config: {} as any
+        config: { headers: {} } as AxiosRequestConfig
       });

You'll need to import AxiosRequestConfig from axios, or use a more complete mock object.

As per coding guidelines: **/*.{ts,tsx,js}: Never use type any or cast to type any.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`
around lines 141 - 142, Replace the unsafe cast "{} as any" used for the config
in the deployment-reader test by importing and using the proper
AxiosRequestConfig type (or constructing a minimal/mock AxiosRequestConfig
object) so the test no longer uses type any; update the spec to import
AxiosRequestConfig from "axios" and change the config declaration passed to the
DeploymentReader test setup (the object currently cast to any) to a correctly
typed AxiosRequestConfig or a typed mock matching the properties your tests
need.
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts (1)

334-344: ⚠️ Potential issue | 🔴 Critical

Type mismatch: fallbackDeploymentReaderService.findAll() expects incompatible parameter structure.

At line 339, you're passing FindAllParams (with nested pagination: { offset, limit, key }) to fallbackDeploymentReaderService.findAll(), which expects DatabaseDeploymentListParams (with flat skip, limit, key at the top level). This type incompatibility causes the fallback service to ignore pagination parameters and always use defaults (skip=0, limit=100), breaking pagination when the blockchain service fails.

🐛 Proposed fix - transform params for fallback service
  private async getDeploymentsList(params: FindAllParams): Promise<DeploymentListResponse> {
    try {
      return await this.deploymentHttpService.findAll(params);
    } catch (error) {
      if (this.shouldFallbackToDatabase(error)) {
-        return await this.fallbackDeploymentReaderService.findAll(params);
+        return await this.fallbackDeploymentReaderService.findAll({
+          owner: params.owner,
+          state: params.state,
+          skip: params.pagination?.offset,
+          limit: params.pagination?.limit,
+          key: params.pagination?.key,
+          countTotal: params.pagination?.countTotal,
+          reverse: params.pagination?.reverse
+        });
      }

      throw error;
    }
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts`
around lines 334 - 344, getDeploymentsList currently passes a FindAllParams
(with nested pagination) into fallbackDeploymentReaderService.findAll which
expects a flat DatabaseDeploymentListParams, causing pagination to be ignored;
fix by mapping/transforming the incoming params before calling
fallbackDeploymentReaderService.findAll (extract pagination.offset → skip,
pagination.limit → limit, pagination.key → key, and copy any other top-level
filters) so you call fallbackDeploymentReaderService.findAll(mappedParams)
instead of passing the original FindAllParams.
🧹 Nitpick comments (1)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts (1)

160-184: Inconsistent use of jest.fn() in a Vitest test file.

The file imports from vitest-mock-extended but the setup() function uses jest.fn() instead of vi.fn(). While this may work if jest globals are shimmed by vitest, it's inconsistent with the project's testing conventions.

♻️ Proposed fix to use vi.fn()

Add the import at the top of the file:

+import { vi } from "vitest";
 import { mock } from "vitest-mock-extended";

Then replace jest.fn() with vi.fn() in the setup function:

     const mocks = {
       providerService: mock<ProviderService>({
-        getLeaseStatus: jest.fn().mockResolvedValue(null),
-        toProviderAuth: jest.fn().mockResolvedValue({ type: "jwt", token: "test" })
+        getLeaseStatus: vi.fn().mockResolvedValue(null),
+        toProviderAuth: vi.fn().mockResolvedValue({ type: "jwt", token: "test" })
       }),
       deploymentHttpService: mock<DeploymentHttpService>({
-        findByOwnerAndDseq: jest.fn().mockResolvedValue(defaultDeploymentInfo),
-        findAll: jest.fn().mockResolvedValue(defaultDeploymentList)
+        findByOwnerAndDseq: vi.fn().mockResolvedValue(defaultDeploymentInfo),
+        findAll: vi.fn().mockResolvedValue(defaultDeploymentList)
       }),
       // ... apply same pattern to other mocks
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`
around lines 160 - 184, The test uses jest.fn() inside the setup() mock objects
(e.g., getLeaseStatus, toProviderAuth, deploymentHttpService.findByOwnerAndDseq,
findAll, leaseHttpService.list, fallbackLeaseReaderService.list,
walletReaderService.getWalletByUserId) but the project uses Vitest; replace all
jest.fn() calls with vi.fn() and ensure vi is available by importing it (import
{ vi } from 'vitest') at the top of the spec file if not already imported so all
mocks use Vitest's vi.fn().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`:
- Around line 141-142: Replace the unsafe cast "{} as any" used for the config
in the deployment-reader test by importing and using the proper
AxiosRequestConfig type (or constructing a minimal/mock AxiosRequestConfig
object) so the test no longer uses type any; update the spec to import
AxiosRequestConfig from "axios" and change the config declaration passed to the
DeploymentReader test setup (the object currently cast to any) to a correctly
typed AxiosRequestConfig or a typed mock matching the properties your tests
need.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts`:
- Around line 334-344: getDeploymentsList currently passes a FindAllParams (with
nested pagination) into fallbackDeploymentReaderService.findAll which expects a
flat DatabaseDeploymentListParams, causing pagination to be ignored; fix by
mapping/transforming the incoming params before calling
fallbackDeploymentReaderService.findAll (extract pagination.offset → skip,
pagination.limit → limit, pagination.key → key, and copy any other top-level
filters) so you call fallbackDeploymentReaderService.findAll(mappedParams)
instead of passing the original FindAllParams.

---

Nitpick comments:
In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`:
- Around line 160-184: The test uses jest.fn() inside the setup() mock objects
(e.g., getLeaseStatus, toProviderAuth, deploymentHttpService.findByOwnerAndDseq,
findAll, leaseHttpService.list, fallbackLeaseReaderService.list,
walletReaderService.getWalletByUserId) but the project uses Vitest; replace all
jest.fn() calls with vi.fn() and ensure vi is available by importing it (import
{ vi } from 'vitest') at the top of the spec file if not already imported so all
mocks use Vitest's vi.fn().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de02f814-3cb7-492d-a426-f1d824a307d4

📥 Commits

Reviewing files that changed from the base of the PR and between 59a50eb and 044b9fb.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
  • packages/http-sdk/package.json
  • packages/http-sdk/src/deployment/deployment-http.service.spec.ts
  • packages/http-sdk/src/deployment/deployment-http.service.ts

@ygrishajev ygrishajev force-pushed the fix/deployment-require-state-for-offset-pagination branch from 044b9fb to 8614677 Compare April 2, 2026 13:29
@ygrishajev ygrishajev force-pushed the fix/deployment-require-state-for-offset-pagination branch from 8614677 to 392fca2 Compare April 2, 2026 14:01
Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (2)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts (1)

114-126: Consider strengthening the assertion to verify offset is absent.

The test verifies owner and state are passed, but doesn't explicitly assert that pagination.offset is undefined/absent when skip is not provided. This would more directly verify the discriminated union branch.

🔧 Suggested enhancement
     it("passes status as state without offset when skip is not provided", async () => {
       const address = "akash1abc";
       const { service, deploymentHttpService } = setup();

       await service.listWithResources({ address, status: "active" });

       expect(deploymentHttpService.findAll).toHaveBeenCalledWith(
         expect.objectContaining({
           owner: address,
-          state: "active"
+          state: "active",
+          pagination: expect.not.objectContaining({ offset: expect.any(Number) })
         })
       );
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`
around lines 114 - 126, The test currently checks owner and state but doesn't
confirm pagination.offset is absent; update the spec for listWithResources to
assert that deploymentHttpService.findAll was called with an object containing
owner: address, state: "active", and a pagination object that does not include
offset (e.g. use expect.objectContaining with pagination:
expect.not.objectContaining({ offset: expect.anything() }) or assert
pagination.offset === undefined) when calling service.listWithResources;
reference the service.listWithResources call and the
deploymentHttpService.findAll expectation to add this assertion.
apps/stats-web/src/app/addresses/[address]/deployments/AddressDeployments.tsx (1)

31-33: Add runtime validation for status filter values.

Line 32 uses as string without validating that the value matches expected statuses. While TanStack Table's UI likely constrains values, the code should guard against unexpected values reaching the API. Narrow the type and validate at the boundary:

Suggested refactor
-  const [statusFilter, setStatusFilter] = useState("active");
+  type DeploymentStatus = "*" | "active" | "closed";
+  const [statusFilter, setStatusFilter] = useState<DeploymentStatus>("active");

   const onColumnFiltersChange = (columnFilters: ColumnFiltersState) => {
-    const statusFilter = (columnFilters.find(filter => filter.id === "status")?.value as string) || "active";
-    setStatusFilter(statusFilter);
+    const rawStatus = columnFilters.find(filter => filter.id === "status")?.value;
+    const nextStatus: DeploymentStatus = ["*", "active", "closed"].includes(rawStatus as string) ? (rawStatus as DeploymentStatus) : "active";
+    setStatusFilter(nextStatus);
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/stats-web/src/app/addresses/`[address]/deployments/AddressDeployments.tsx
around lines 31 - 33, The onColumnFiltersChange handler currently blindly casts
filter.value to string for the "status" filter; add runtime validation in
onColumnFiltersChange (and before calling setStatusFilter) by extracting the
value from the ColumnFiltersState, checking typeof value === "string" and that
it exists in an allowedStatusValues array (e.g. const allowedStatusValues =
["active","inactive","all"] or your app's canonical statuses), and if it is
invalid fall back to the default "active" (or another canonical default) before
calling setStatusFilter; this keeps the boundary around the "status" filter safe
and avoids sending unexpected values to the API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts`:
- Around line 114-126: The test currently checks owner and state but doesn't
confirm pagination.offset is absent; update the spec for listWithResources to
assert that deploymentHttpService.findAll was called with an object containing
owner: address, state: "active", and a pagination object that does not include
offset (e.g. use expect.objectContaining with pagination:
expect.not.objectContaining({ offset: expect.anything() }) or assert
pagination.offset === undefined) when calling service.listWithResources;
reference the service.listWithResources call and the
deploymentHttpService.findAll expectation to add this assertion.

In
`@apps/stats-web/src/app/addresses/`[address]/deployments/AddressDeployments.tsx:
- Around line 31-33: The onColumnFiltersChange handler currently blindly casts
filter.value to string for the "status" filter; add runtime validation in
onColumnFiltersChange (and before calling setStatusFilter) by extracting the
value from the ColumnFiltersState, checking typeof value === "string" and that
it exists in an allowedStatusValues array (e.g. const allowedStatusValues =
["active","inactive","all"] or your app's canonical statuses), and if it is
invalid fall back to the default "active" (or another canonical default) before
calling setStatusFilter; this keeps the boundary around the "status" filter safe
and avoids sending unexpected values to the API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2bb1b5ac-834f-4baf-b9c1-3028f01091e3

📥 Commits

Reviewing files that changed from the base of the PR and between 8614677 and 392fca2.

⛔ Files ignored due to path filters (2)
  • apps/api/test/functional/__snapshots__/docs.spec.ts.snap is excluded by !**/*.snap
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • apps/api/src/deployment/http-schemas/deployment.schema.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.spec.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
  • apps/api/test/functional/addresses.spec.ts
  • apps/api/test/functional/deployments.spec.ts
  • apps/stats-web/src/app/addresses/[address]/deployments/AddressDeployments.tsx
  • packages/http-sdk/package.json
  • packages/http-sdk/src/deployment/deployment-http.service.spec.ts
  • packages/http-sdk/src/deployment/deployment-http.service.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/http-sdk/package.json
  • packages/http-sdk/src/deployment/deployment-http.service.spec.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/api/test/functional/addresses.spec.ts
  • apps/api/test/functional/deployments.spec.ts
  • packages/http-sdk/src/deployment/deployment-http.service.ts

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.

1 participant