From 1e64dce68883f5341af22a99bec8f27eb6163863 Mon Sep 17 00:00:00 2001 From: Sam Peters Date: Wed, 8 Nov 2023 11:48:08 -0600 Subject: [PATCH] chore: address pr feedback - remove use of graphql types --- .../app/accounts/get-invoices-for-account.ts | 13 ++- core/api/src/app/errors.ts | 2 - .../app/wallets/get-invoices-for-wallets.ts | 25 +++--- core/api/src/domain/ledger/index.types.d.ts | 2 + core/api/src/domain/pagination/errors.ts | 2 - core/api/src/domain/pagination/index.ts | 60 -------------- .../src/domain/pagination/index.types.d.ts | 19 ----- core/api/src/domain/primitives/index.ts | 76 ++++++++++++++++- .../src/domain/primitives/index.types.d.ts | 27 ++++++ .../domain/wallet-invoices/index.types.d.ts | 6 +- core/api/src/graphql/error-map.ts | 2 +- .../graphql/public/types/abstract/account.ts | 1 - .../graphql/shared/types/object/btc-wallet.ts | 2 +- .../graphql/shared/types/object/usd-wallet.ts | 2 +- .../src/services/mongoose/wallet-invoices.ts | 21 +++-- .../wallet-invoices-repository.spec.ts | 17 ++-- .../pagination/parse-pagination-args.spec.ts | 65 --------------- .../primitives/parse-pagination-args.spec.ts | 83 +++++++++++++++++++ 18 files changed, 240 insertions(+), 185 deletions(-) delete mode 100644 core/api/src/domain/pagination/errors.ts delete mode 100644 core/api/src/domain/pagination/index.ts delete mode 100644 core/api/src/domain/pagination/index.types.d.ts delete mode 100644 core/api/test/unit/domain/pagination/parse-pagination-args.spec.ts create mode 100644 core/api/test/unit/domain/primitives/parse-pagination-args.spec.ts diff --git a/core/api/src/app/accounts/get-invoices-for-account.ts b/core/api/src/app/accounts/get-invoices-for-account.ts index 4f4c20bd52e..b1701188d6c 100644 --- a/core/api/src/app/accounts/get-invoices-for-account.ts +++ b/core/api/src/app/accounts/get-invoices-for-account.ts @@ -6,12 +6,17 @@ import { WalletsRepository } from "@/services/mongoose" export const getInvoicesForAccountByWalletIds = async ({ account, walletIds, - paginationArgs, + rawPaginationArgs, }: { account: Account walletIds?: WalletId[] - paginationArgs?: PaginationArgs -}): Promise | ApplicationError> => { + rawPaginationArgs: { + first?: number | null + last?: number | null + before?: string | null + after?: string | null + } +}): Promise | ApplicationError> => { const walletsRepo = WalletsRepository() const accountWallets = await walletsRepo.listByAccountId(account.id) @@ -21,5 +26,5 @@ export const getInvoicesForAccountByWalletIds = async ({ walletIds ? walletIds.includes(wallet.id) : true, ) - return getInvoicesForWallets({ wallets, paginationArgs }) + return getInvoicesForWallets({ wallets, rawPaginationArgs }) } diff --git a/core/api/src/app/errors.ts b/core/api/src/app/errors.ts index 5192cb81667..46c160f5d0c 100644 --- a/core/api/src/app/errors.ts +++ b/core/api/src/app/errors.ts @@ -20,7 +20,6 @@ import * as CaptchaErrors from "@/domain/captcha/errors" import * as AuthenticationErrors from "@/domain/authentication/errors" import * as UserErrors from "@/domain/users/errors" import * as WalletInvoiceErrors from "@/domain/wallet-invoices/errors" -import * as PaginationErrors from "@/domain/pagination/errors" import * as LedgerFacadeErrors from "@/services/ledger/domain/errors" import * as KratosErrors from "@/services/kratos/errors" @@ -50,7 +49,6 @@ export const ApplicationErrors = { ...AuthenticationErrors, ...UserErrors, ...WalletInvoiceErrors, - ...PaginationErrors, ...KratosErrors, ...LedgerFacadeErrors, diff --git a/core/api/src/app/wallets/get-invoices-for-wallets.ts b/core/api/src/app/wallets/get-invoices-for-wallets.ts index ce42b7c2eac..fc28c9383b2 100644 --- a/core/api/src/app/wallets/get-invoices-for-wallets.ts +++ b/core/api/src/app/wallets/get-invoices-for-wallets.ts @@ -1,27 +1,32 @@ import { MAX_PAGINATION_PAGE_SIZE } from "@/config" -import { parsePaginationArgs } from "@/domain/pagination" +import { checkedToPaginatedQueryArgs } from "@/domain/primitives" import { WalletInvoicesRepository } from "@/services/mongoose" export const getInvoicesForWallets = async ({ wallets, - paginationArgs, + rawPaginationArgs, }: { wallets: Wallet[] - paginationArgs?: PaginationArgs -}): Promise | ApplicationError> => { + rawPaginationArgs: { + first?: number | null + last?: number | null + before?: string | null + after?: string | null + } +}): Promise | ApplicationError> => { const walletIds = wallets.map((wallet) => wallet.id) - const parsedPaginationArgs = parsePaginationArgs({ - paginationArgs, + const paginationArgs = checkedToPaginatedQueryArgs({ + paginationArgs: rawPaginationArgs, maxPageSize: MAX_PAGINATION_PAGE_SIZE, }) - if (parsedPaginationArgs instanceof Error) { - return parsedPaginationArgs + if (paginationArgs instanceof Error) { + return paginationArgs } - return WalletInvoicesRepository().getInvoicesForWallets({ + return WalletInvoicesRepository().findInvoicesForWallets({ walletIds, - paginationArgs: parsedPaginationArgs, + paginationArgs, }) } diff --git a/core/api/src/domain/ledger/index.types.d.ts b/core/api/src/domain/ledger/index.types.d.ts index 54c9a77edd4..f837301d524 100644 --- a/core/api/src/domain/ledger/index.types.d.ts +++ b/core/api/src/domain/ledger/index.types.d.ts @@ -2,6 +2,8 @@ type LedgerError = import("./errors").LedgerError type FeeDifferenceError = import("./errors").FeeDifferenceError type LedgerServiceError = import("./errors").LedgerServiceError +type PaginationArgs = import("graphql-relay").ConnectionArguments + declare const liabilitiesWalletId: unique symbol type LiabilitiesWalletId = string & { [liabilitiesWalletId]: never } diff --git a/core/api/src/domain/pagination/errors.ts b/core/api/src/domain/pagination/errors.ts deleted file mode 100644 index 78ff3000948..00000000000 --- a/core/api/src/domain/pagination/errors.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { ValidationError } from "@/domain/shared/errors" -export class InvalidPaginationArgsError extends ValidationError {} diff --git a/core/api/src/domain/pagination/index.ts b/core/api/src/domain/pagination/index.ts deleted file mode 100644 index 0c49f258b21..00000000000 --- a/core/api/src/domain/pagination/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { InvalidPaginationArgsError } from "./errors" - -export const parsePaginationArgs = ({ - paginationArgs, - maxPageSize, -}: { - paginationArgs?: PaginationArgs - maxPageSize: number -}): InvalidPaginationArgsError | ParsedPaginationArgs => { - const { first, last, before, after } = paginationArgs || {} - if (first && last) { - return new InvalidPaginationArgsError(`Cannot use both "first" and "last".`) - } - - if (typeof first === "number") { - if (first > maxPageSize) { - return new InvalidPaginationArgsError( - `Requested page size (${first}) is greater than max allowed page size ${maxPageSize}.`, - ) - } - - if (first <= 0) { - return new InvalidPaginationArgsError( - `Requested page size (${first}) must be greater than 0.`, - ) - } - - return { - first, - after, - before, - } - } - - if (typeof last === "number") { - if (last > maxPageSize) { - return new InvalidPaginationArgsError( - `Requested page size (${last}) is greater than max allowed page size ${maxPageSize}.`, - ) - } - - if (last <= 0) { - return new InvalidPaginationArgsError( - `Requested page size (${last}) must be greater than 0.`, - ) - } - - return { - last, - before, - after, - } - } - - return { - first: maxPageSize, - after, - before, - } -} diff --git a/core/api/src/domain/pagination/index.types.d.ts b/core/api/src/domain/pagination/index.types.d.ts deleted file mode 100644 index 48b3041907a..00000000000 --- a/core/api/src/domain/pagination/index.types.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -type PaginatedResult = import("graphql-relay").Connection - -type PaginationArgs = import("graphql-relay").ConnectionArguments - -type ConnectionCursor = import("graphql-relay").ConnectionCursor - -type ParsedPaginationArgs = - | { - first: number - last?: undefined - after?: ConnectionCursor | null - before?: ConnectionCursor | null - } - | { - first?: undefined - last: number - before?: ConnectionCursor | null - after?: ConnectionCursor | null - } diff --git a/core/api/src/domain/primitives/index.ts b/core/api/src/domain/primitives/index.ts index 28800d6aa2e..ed99d5b55e4 100644 --- a/core/api/src/domain/primitives/index.ts +++ b/core/api/src/domain/primitives/index.ts @@ -1,5 +1,4 @@ -import { InvalidMinutesError } from "@/domain/errors" - +import { InvalidMinutesError, InvalidPaginatedQueryArgsError } from "@/domain/errors" export const toSeconds = (seconds: number): Seconds => { return seconds as Seconds } @@ -13,3 +12,76 @@ export const checkedToMinutes = (minutes: number): Minutes | ValidationError => if (!isMinutes) return new InvalidMinutesError(`Invalid value for minutes: ${minutes}`) return minutes as Minutes } + +export const checkedToPaginatedQueryCursor = (cursor: string): PaginatedQueryCursor => { + return cursor as PaginatedQueryCursor +} + +export const checkedToPaginatedQueryArgs = ({ + paginationArgs, + maxPageSize, +}: { + paginationArgs: { + first?: number | null + last?: number | null + before?: string | null + after?: string | null + } + maxPageSize: number +}): InvalidPaginatedQueryArgsError | PaginatedQueryArgs => { + const { first, last, before, after } = paginationArgs || {} + if (first && last) { + return new InvalidPaginatedQueryArgsError(`Cannot use both "first" and "last".`) + } + + const afterCursor = + typeof after === "string" ? checkedToPaginatedQueryCursor(after) : undefined + const beforeCursor = + typeof before === "string" ? checkedToPaginatedQueryCursor(before) : undefined + + if (typeof first === "number") { + if (first > maxPageSize) { + return new InvalidPaginatedQueryArgsError( + `Requested page size (${first}) is greater than max allowed page size ${maxPageSize}.`, + ) + } + + if (first <= 0) { + return new InvalidPaginatedQueryArgsError( + `Requested page size (${first}) must be greater than 0.`, + ) + } + + return { + first, + after: afterCursor, + before: beforeCursor, + } + } + + if (typeof last === "number") { + if (last > maxPageSize) { + return new InvalidPaginatedQueryArgsError( + `Requested page size (${last}) is greater than max allowed page size ${maxPageSize}.`, + ) + } + + if (last <= 0) { + return new InvalidPaginatedQueryArgsError( + `Requested page size (${last}) must be greater than 0.`, + ) + } + + return { + last, + after: afterCursor, + before: beforeCursor, + } + } + + return { + first: maxPageSize, + after: afterCursor, + before: beforeCursor, + } +} diff --git a/core/api/src/domain/primitives/index.types.d.ts b/core/api/src/domain/primitives/index.types.d.ts index 9a3a8394dc4..3ebd982335d 100644 --- a/core/api/src/domain/primitives/index.types.d.ts +++ b/core/api/src/domain/primitives/index.types.d.ts @@ -28,3 +28,30 @@ type JSONValue = | undefined type JSONArray = Array type JSONObject = { [key: string]: JSONValue } + +type PaginatedQueryCursor = string & { readonly brand: unique symbol } +type PaginatedQueryArgs = + | { + first: number + last?: undefined + after?: PaginatedQueryCursor + before?: PaginatedQueryCursor + } + | { + first?: undefined + last: number + before?: PaginatedQueryCursor + after?: PaginatedQueryCursor + } +type PaginatedQueryResult = { + edges: { + node: T + cursor: PaginatedQueryCursor + }[] + pageInfo: { + startCursor?: PaginatedQueryCursor + endCursor?: PaginatedQueryCursor + hasNextPage: boolean + hasPreviousPage: boolean + } +} diff --git a/core/api/src/domain/wallet-invoices/index.types.d.ts b/core/api/src/domain/wallet-invoices/index.types.d.ts index 7d4f568e8de..4c2368fe53e 100644 --- a/core/api/src/domain/wallet-invoices/index.types.d.ts +++ b/core/api/src/domain/wallet-invoices/index.types.d.ts @@ -179,13 +179,13 @@ interface IWalletInvoicesRepository { paymentHash: PaymentHash, ) => Promise - getInvoicesForWallets: ({ + findInvoicesForWallets: ({ walletIds, paginationArgs, }: { walletIds: WalletId[] - paginationArgs: ParsedPaginationArgs - }) => Promise | RepositoryError> + paginationArgs: PaginatedQueryArgs + }) => Promise | RepositoryError> findByPaymentHash: ( paymentHash: PaymentHash, diff --git a/core/api/src/graphql/error-map.ts b/core/api/src/graphql/error-map.ts index 8eef74e32a0..3aff240216c 100644 --- a/core/api/src/graphql/error-map.ts +++ b/core/api/src/graphql/error-map.ts @@ -446,7 +446,7 @@ export const mapError = (error: ApplicationError): CustomGraphQLError => { case "UnauthorizedIPMetadataCountryError": return new UnauthorizedIPMetadataCountryError({ logger: baseLogger }) - case "InvalidPaginationArgsError": + case "InvalidPaginatedQueryArgsError": message = error.message return new ValidationInternalError({ message, logger: baseLogger }) diff --git a/core/api/src/graphql/public/types/abstract/account.ts b/core/api/src/graphql/public/types/abstract/account.ts index 61b4f98c678..c084ef14c47 100644 --- a/core/api/src/graphql/public/types/abstract/account.ts +++ b/core/api/src/graphql/public/types/abstract/account.ts @@ -65,7 +65,6 @@ const IAccount = GT.Interface({ pendingTransactions: { type: GT.NonNullList(Transaction), args: { - ...connectionArgs, walletIds: { type: GT.List(WalletId), }, diff --git a/core/api/src/graphql/shared/types/object/btc-wallet.ts b/core/api/src/graphql/shared/types/object/btc-wallet.ts index a4d6150219c..5fca6fca9ef 100644 --- a/core/api/src/graphql/shared/types/object/btc-wallet.ts +++ b/core/api/src/graphql/shared/types/object/btc-wallet.ts @@ -135,7 +135,7 @@ const BtcWallet = GT.Object({ resolve: async (source, args) => { const result = await Wallets.getInvoicesForWallets({ wallets: [source], - paginationArgs: args, + rawPaginationArgs: args, }) if (result instanceof Error) { diff --git a/core/api/src/graphql/shared/types/object/usd-wallet.ts b/core/api/src/graphql/shared/types/object/usd-wallet.ts index c0b5c9544eb..d493d5d31bb 100644 --- a/core/api/src/graphql/shared/types/object/usd-wallet.ts +++ b/core/api/src/graphql/shared/types/object/usd-wallet.ts @@ -133,7 +133,7 @@ const UsdWallet = GT.Object({ resolve: async (source, args) => { const result = await Wallets.getInvoicesForWallets({ wallets: [source], - paginationArgs: args, + rawPaginationArgs: args, }) if (result instanceof Error) { diff --git a/core/api/src/services/mongoose/wallet-invoices.ts b/core/api/src/services/mongoose/wallet-invoices.ts index 7a27bd4b663..f6cbd8821ef 100644 --- a/core/api/src/services/mongoose/wallet-invoices.ts +++ b/core/api/src/services/mongoose/wallet-invoices.ts @@ -10,6 +10,7 @@ import { UnknownRepositoryError, WalletInvoiceMissingLnInvoiceError, } from "@/domain/errors" +import { checkedToPaginatedQueryCursor } from "@/domain/primitives" import { UsdPaymentAmount } from "@/domain/shared" export const WalletInvoicesRepository = (): IWalletInvoicesRepository => { @@ -137,13 +138,13 @@ export const WalletInvoicesRepository = (): IWalletInvoicesRepository => { } } - const getInvoicesForWallets = async ({ + const findInvoicesForWallets = async ({ walletIds, paginationArgs, }: { walletIds: WalletId[] - paginationArgs: ParsedPaginationArgs - }): Promise | RepositoryError> => { + paginationArgs: PaginatedQueryArgs + }): Promise | RepositoryError> => { const { first, last, before, after } = paginationArgs try { @@ -222,12 +223,18 @@ export const WalletInvoicesRepository = (): IWalletInvoicesRepository => { return { edges: walletInvoices.map((walletInvoice) => ({ - cursor: walletInvoice.paymentHash, + cursor: checkedToPaginatedQueryCursor(walletInvoice.paymentHash), node: walletInvoice, })), pageInfo: { - startCursor: walletInvoices[0]?.paymentHash ?? null, - endCursor: walletInvoices[walletInvoices.length - 1]?.paymentHash ?? null, + startCursor: walletInvoices[0]?.paymentHash + ? checkedToPaginatedQueryCursor(walletInvoices[0].paymentHash) + : undefined, + endCursor: walletInvoices[walletInvoices.length - 1]?.paymentHash + ? checkedToPaginatedQueryCursor( + walletInvoices[walletInvoices.length - 1].paymentHash, + ) + : undefined, hasPreviousPage, hasNextPage, }, @@ -245,7 +252,7 @@ export const WalletInvoicesRepository = (): IWalletInvoicesRepository => { yieldPending, deleteByPaymentHash, deleteUnpaidOlderThan, - getInvoicesForWallets, + findInvoicesForWallets, } } diff --git a/core/api/test/integration/services/wallet-invoices-repository.spec.ts b/core/api/test/integration/services/wallet-invoices-repository.spec.ts index e5386d48bcb..32a58e50331 100644 --- a/core/api/test/integration/services/wallet-invoices-repository.spec.ts +++ b/core/api/test/integration/services/wallet-invoices-repository.spec.ts @@ -1,3 +1,4 @@ +import { checkedToPaginatedQueryCursor } from "@/domain/primitives" import { WalletCurrency } from "@/domain/shared" import { WalletInvoicesRepository } from "@/services/mongoose" import { createMockWalletInvoice } from "test/helpers/wallet-invoices" @@ -40,7 +41,7 @@ describe("WalletInvoicesRepository", () => { const walletInvoicesRepository = WalletInvoicesRepository() it("gets first page of invoices", async () => { - const result = await walletInvoicesRepository.getInvoicesForWallets({ + const result = await walletInvoicesRepository.findInvoicesForWallets({ walletIds: [recipientWalletDescriptor.id], paginationArgs: { first: 100, @@ -68,11 +69,11 @@ describe("WalletInvoicesRepository", () => { }) it("gets page after cursor", async () => { - const result = await walletInvoicesRepository.getInvoicesForWallets({ + const result = await walletInvoicesRepository.findInvoicesForWallets({ walletIds: [recipientWalletDescriptor.id], paginationArgs: { first: 2, - after: createdInvoices[1].paymentHash, + after: checkedToPaginatedQueryCursor(createdInvoices[1].paymentHash), }, }) if (result instanceof Error) throw result @@ -100,7 +101,7 @@ describe("WalletInvoicesRepository", () => { }) it("get last page of invoices", async () => { - const result = await walletInvoicesRepository.getInvoicesForWallets({ + const result = await walletInvoicesRepository.findInvoicesForWallets({ walletIds: [recipientWalletDescriptor.id], paginationArgs: { last: 100, @@ -119,11 +120,13 @@ describe("WalletInvoicesRepository", () => { }) it("get page before cursor", async () => { - const result = await walletInvoicesRepository.getInvoicesForWallets({ + const result = await walletInvoicesRepository.findInvoicesForWallets({ walletIds: [recipientWalletDescriptor.id], paginationArgs: { last: 2, - before: createdInvoices[createdInvoices.length - 2].paymentHash, + before: checkedToPaginatedQueryCursor( + createdInvoices[createdInvoices.length - 2].paymentHash, + ), }, }) if (result instanceof Error) throw result @@ -151,7 +154,7 @@ describe("WalletInvoicesRepository", () => { }) it("returns empty edges for wallet without invoices", async () => { - const result = await walletInvoicesRepository.getInvoicesForWallets({ + const result = await walletInvoicesRepository.findInvoicesForWallets({ walletIds: [crypto.randomUUID() as WalletId], paginationArgs: { first: 100, diff --git a/core/api/test/unit/domain/pagination/parse-pagination-args.spec.ts b/core/api/test/unit/domain/pagination/parse-pagination-args.spec.ts deleted file mode 100644 index 3281ef89d3a..00000000000 --- a/core/api/test/unit/domain/pagination/parse-pagination-args.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { parsePaginationArgs } from "@/domain/pagination" -import { InvalidPaginationArgsError } from "@/domain/pagination/errors" - -describe("parsePaginationArgs", () => { - const maxPageSize = 100 - - it("should return max page size when no pagination args are provided", () => { - const result = parsePaginationArgs({ maxPageSize }) - expect(result).toEqual({ first: maxPageSize, after: undefined, before: undefined }) - }) - - it('should return error when both "first" and "last" are provided', () => { - const result = parsePaginationArgs({ - paginationArgs: { first: 10, last: 10 }, - maxPageSize, - }) - expect(result).toBeInstanceOf(InvalidPaginationArgsError) - }) - - it('should return error when "first" is greater than max page size', () => { - const result = parsePaginationArgs({ paginationArgs: { first: 101 }, maxPageSize }) - expect(result).toBeInstanceOf(InvalidPaginationArgsError) - }) - - it('should return error when "first" is less than or equal to 0', () => { - const result = parsePaginationArgs({ paginationArgs: { first: 0 }, maxPageSize }) - expect(result).toBeInstanceOf(InvalidPaginationArgsError) - }) - - it('should return valid parsed args when "first" is within valid range', () => { - const first = 50 - const result = parsePaginationArgs({ paginationArgs: { first }, maxPageSize }) - expect(result).toEqual({ first, after: undefined, before: undefined }) - }) - - it('should return error when "last" is greater than max page size', () => { - const result = parsePaginationArgs({ paginationArgs: { last: 101 }, maxPageSize }) - expect(result).toBeInstanceOf(InvalidPaginationArgsError) - }) - - it('should return error when "last" is less than or equal to 0', () => { - const result = parsePaginationArgs({ paginationArgs: { last: -1 }, maxPageSize }) - expect(result).toBeInstanceOf(InvalidPaginationArgsError) - }) - - it('should return valid parsed args when "last" is within valid range', () => { - const last = 50 - const result = parsePaginationArgs({ paginationArgs: { last }, maxPageSize }) - expect(result).toEqual({ last, before: undefined, after: undefined }) - }) - - it('should handle "after" cursor when provided with "first"', () => { - const after = "cursor" - const first = 10 - const result = parsePaginationArgs({ paginationArgs: { after, first }, maxPageSize }) - expect(result).toEqual({ first, after, before: undefined }) - }) - - it('should handle "before" cursor when provided with "last"', () => { - const before = "cursor" - const last = 10 - const result = parsePaginationArgs({ paginationArgs: { before, last }, maxPageSize }) - expect(result).toEqual({ last, before, after: undefined }) - }) -}) diff --git a/core/api/test/unit/domain/primitives/parse-pagination-args.spec.ts b/core/api/test/unit/domain/primitives/parse-pagination-args.spec.ts new file mode 100644 index 00000000000..3cd0adade39 --- /dev/null +++ b/core/api/test/unit/domain/primitives/parse-pagination-args.spec.ts @@ -0,0 +1,83 @@ +import { checkedToPaginatedQueryArgs } from "@/domain/primitives" +import { InvalidPaginatedQueryArgsError } from "@/domain/errors" + +describe("checkedToPaginatedQueryArgs", () => { + const maxPageSize = 100 + + it("should return max page size when no pagination args are provided", () => { + const result = checkedToPaginatedQueryArgs({ maxPageSize, paginationArgs: {} }) + expect(result).toEqual({ first: maxPageSize, after: undefined, before: undefined }) + }) + + it('should return error when both "first" and "last" are provided', () => { + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { first: 10, last: 10 }, + maxPageSize, + }) + expect(result).toBeInstanceOf(InvalidPaginatedQueryArgsError) + }) + + it('should return error when "first" is greater than max page size', () => { + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { first: 101 }, + maxPageSize, + }) + expect(result).toBeInstanceOf(InvalidPaginatedQueryArgsError) + }) + + it('should return error when "first" is less than or equal to 0', () => { + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { first: 0 }, + maxPageSize, + }) + expect(result).toBeInstanceOf(InvalidPaginatedQueryArgsError) + }) + + it('should return valid parsed args when "first" is within valid range', () => { + const first = 50 + const result = checkedToPaginatedQueryArgs({ paginationArgs: { first }, maxPageSize }) + expect(result).toEqual({ first, after: undefined, before: undefined }) + }) + + it('should return error when "last" is greater than max page size', () => { + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { last: 101 }, + maxPageSize, + }) + expect(result).toBeInstanceOf(InvalidPaginatedQueryArgsError) + }) + + it('should return error when "last" is less than or equal to 0', () => { + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { last: -1 }, + maxPageSize, + }) + expect(result).toBeInstanceOf(InvalidPaginatedQueryArgsError) + }) + + it('should return valid parsed args when "last" is within valid range', () => { + const last = 50 + const result = checkedToPaginatedQueryArgs({ paginationArgs: { last }, maxPageSize }) + expect(result).toEqual({ last, before: undefined, after: undefined }) + }) + + it('should handle "after" cursor when provided with "first"', () => { + const after = "cursor" + const first = 10 + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { after, first }, + maxPageSize, + }) + expect(result).toEqual({ first, after, before: undefined }) + }) + + it('should handle "before" cursor when provided with "last"', () => { + const before = "cursor" + const last = 10 + const result = checkedToPaginatedQueryArgs({ + paginationArgs: { before, last }, + maxPageSize, + }) + expect(result).toEqual({ last, before, after: undefined }) + }) +})