diff --git a/account.go b/account.go index b25ebb4..a8002b0 100644 --- a/account.go +++ b/account.go @@ -55,7 +55,7 @@ func (r *AccountService) Update(ctx context.Context, accountToken string, body A } // List account configurations. -func (r *AccountService) List(ctx context.Context, query AccountListParams, opts ...option.RequestOption) (res *shared.Page[Account], err error) { +func (r *AccountService) List(ctx context.Context, query AccountListParams, opts ...option.RequestOption) (res *shared.CursorPage[Account], err error) { var raw *http.Response opts = append(r.Options, opts...) opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) @@ -73,8 +73,8 @@ func (r *AccountService) List(ctx context.Context, query AccountListParams, opts } // List account configurations. -func (r *AccountService) ListAutoPaging(ctx context.Context, query AccountListParams, opts ...option.RequestOption) *shared.PageAutoPager[Account] { - return shared.NewPageAutoPager(r.List(ctx, query, opts...)) +func (r *AccountService) ListAutoPaging(ctx context.Context, query AccountListParams, opts ...option.RequestOption) *shared.CursorPageAutoPager[Account] { + return shared.NewCursorPageAutoPager(r.List(ctx, query, opts...)) } type Account struct { @@ -283,10 +283,14 @@ type AccountListParams struct { // Date string in RFC 3339 format. Only entries created before the specified date // will be included. UTC time zone. End param.Field[time.Time] `query:"end" format:"date-time"` - // Page (for pagination). - Page param.Field[int64] `query:"page"` + // A cursor representing an item's token before which a page of results should end. + // Used to retrieve the previous page of results before this item. + EndingBefore param.Field[string] `query:"ending_before"` // Page size (for pagination). PageSize param.Field[int64] `query:"page_size"` + // A cursor representing an item's token after which a page of results should + // begin. Used to retrieve the next page of results after this item. + StartingAfter param.Field[string] `query:"starting_after"` } // URLQuery serializes [AccountListParams]'s query parameters as `url.Values`. diff --git a/account_test.go b/account_test.go index d605263..f7ea645 100644 --- a/account_test.go +++ b/account_test.go @@ -89,10 +89,11 @@ func TestAccountListWithOptionalParams(t *testing.T) { option.WithAPIKey("APIKey"), ) _, err := client.Accounts.List(context.TODO(), lithic.AccountListParams{ - Begin: lithic.F(time.Now()), - End: lithic.F(time.Now()), - Page: lithic.F(int64(0)), - PageSize: lithic.F(int64(1)), + Begin: lithic.F(time.Now()), + End: lithic.F(time.Now()), + EndingBefore: lithic.F("string"), + PageSize: lithic.F(int64(1)), + StartingAfter: lithic.F("string"), }) if err != nil { var apierr *lithic.Error diff --git a/api.md b/api.md index 8377fc5..09bc165 100644 --- a/api.md +++ b/api.md @@ -28,7 +28,7 @@ Methods: - client.Accounts.Get(ctx context.Context, accountToken string) (lithic.Account, error) - client.Accounts.Update(ctx context.Context, accountToken string, body lithic.AccountUpdateParams) (lithic.Account, error) -- client.Accounts.List(ctx context.Context, query lithic.AccountListParams) (shared.Page[lithic.Account], error) +- client.Accounts.List(ctx context.Context, query lithic.AccountListParams) (shared.CursorPage[lithic.Account], error) # AccountHolders @@ -62,7 +62,7 @@ Methods: - client.AuthRules.New(ctx context.Context, body lithic.AuthRuleNewParams) (lithic.AuthRule, error) - client.AuthRules.Get(ctx context.Context, authRuleToken string) (lithic.AuthRuleGetResponse, error) - client.AuthRules.Update(ctx context.Context, authRuleToken string, body lithic.AuthRuleUpdateParams) (lithic.AuthRule, error) -- client.AuthRules.List(ctx context.Context, query lithic.AuthRuleListParams) (shared.Page[lithic.AuthRule], error) +- client.AuthRules.List(ctx context.Context, query lithic.AuthRuleListParams) (shared.CursorPage[lithic.AuthRule], error) - client.AuthRules.Apply(ctx context.Context, authRuleToken string, body lithic.AuthRuleApplyParams) (lithic.AuthRule, error) - client.AuthRules.Remove(ctx context.Context, body lithic.AuthRuleRemoveParams) (lithic.AuthRuleRemoveResponse, error) @@ -121,7 +121,7 @@ Methods: - client.Cards.New(ctx context.Context, body lithic.CardNewParams) (lithic.Card, error) - client.Cards.Get(ctx context.Context, cardToken string) (lithic.Card, error) - client.Cards.Update(ctx context.Context, cardToken string, body lithic.CardUpdateParams) (lithic.Card, error) -- client.Cards.List(ctx context.Context, query lithic.CardListParams) (shared.Page[lithic.Card], error) +- client.Cards.List(ctx context.Context, query lithic.CardListParams) (shared.CursorPage[lithic.Card], error) - client.Cards.Embed(ctx context.Context, query lithic.CardEmbedParams) (string, error) - client.Cards.Provision(ctx context.Context, cardToken string, body lithic.CardProvisionParams) (lithic.CardProvisionResponse, error) - client.Cards.Reissue(ctx context.Context, cardToken string, body lithic.CardReissueParams) (lithic.Card, error) @@ -259,7 +259,7 @@ Response Types: Methods: - client.Transactions.Get(ctx context.Context, transactionToken string) (lithic.Transaction, error) -- client.Transactions.List(ctx context.Context, query lithic.TransactionListParams) (shared.Page[lithic.Transaction], error) +- client.Transactions.List(ctx context.Context, query lithic.TransactionListParams) (shared.CursorPage[lithic.Transaction], error) - client.Transactions.SimulateAuthorization(ctx context.Context, body lithic.TransactionSimulateAuthorizationParams) (lithic.TransactionSimulateAuthorizationResponse, error) - client.Transactions.SimulateAuthorizationAdvice(ctx context.Context, body lithic.TransactionSimulateAuthorizationAdviceParams) (lithic.TransactionSimulateAuthorizationAdviceResponse, error) - client.Transactions.SimulateClearing(ctx context.Context, body lithic.TransactionSimulateClearingParams) (lithic.TransactionSimulateClearingResponse, error) diff --git a/authrule.go b/authrule.go index 27520dd..5dffab6 100644 --- a/authrule.go +++ b/authrule.go @@ -61,7 +61,7 @@ func (r *AuthRuleService) Update(ctx context.Context, authRuleToken string, body } // Return all of the Auth Rules under the program. -func (r *AuthRuleService) List(ctx context.Context, query AuthRuleListParams, opts ...option.RequestOption) (res *shared.Page[AuthRule], err error) { +func (r *AuthRuleService) List(ctx context.Context, query AuthRuleListParams, opts ...option.RequestOption) (res *shared.CursorPage[AuthRule], err error) { var raw *http.Response opts = append(r.Options, opts...) opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) @@ -79,8 +79,8 @@ func (r *AuthRuleService) List(ctx context.Context, query AuthRuleListParams, op } // Return all of the Auth Rules under the program. -func (r *AuthRuleService) ListAutoPaging(ctx context.Context, query AuthRuleListParams, opts ...option.RequestOption) *shared.PageAutoPager[AuthRule] { - return shared.NewPageAutoPager(r.List(ctx, query, opts...)) +func (r *AuthRuleService) ListAutoPaging(ctx context.Context, query AuthRuleListParams, opts ...option.RequestOption) *shared.CursorPageAutoPager[AuthRule] { + return shared.NewCursorPageAutoPager(r.List(ctx, query, opts...)) } // Applies an existing authorization rule (Auth Rule) to an program, account, or @@ -103,7 +103,9 @@ func (r *AuthRuleService) Remove(ctx context.Context, body AuthRuleRemoveParams, type AuthRule struct { // Globally unique identifier. - Token string `json:"token" format:"uuid"` + Token string `json:"token,required" format:"uuid"` + // Indicates whether the Auth Rule is ACTIVE or INACTIVE + State AuthRuleState `json:"state,required"` // Array of account_token(s) identifying the accounts that the Auth Rule applies // to. Note that only this field or `card_tokens` can be provided for a given Auth // Rule. @@ -124,14 +126,13 @@ type AuthRule struct { CardTokens []string `json:"card_tokens"` // Boolean indicating whether the Auth Rule is applied at the program level. ProgramLevel bool `json:"program_level"` - // Indicates whether the Auth Rule is ACTIVE or INACTIVE - State AuthRuleState `json:"state"` - JSON authRuleJSON + JSON authRuleJSON } // authRuleJSON contains the JSON metadata for the struct [AuthRule] type authRuleJSON struct { Token apijson.Field + State apijson.Field AccountTokens apijson.Field AllowedCountries apijson.Field AllowedMcc apijson.Field @@ -139,7 +140,6 @@ type authRuleJSON struct { BlockedMcc apijson.Field CardTokens apijson.Field ProgramLevel apijson.Field - State apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -244,10 +244,14 @@ func (r AuthRuleUpdateParams) MarshalJSON() (data []byte, err error) { } type AuthRuleListParams struct { - // Page (for pagination). - Page param.Field[int64] `query:"page"` + // A cursor representing an item's token before which a page of results should end. + // Used to retrieve the previous page of results before this item. + EndingBefore param.Field[string] `query:"ending_before"` // Page size (for pagination). PageSize param.Field[int64] `query:"page_size"` + // A cursor representing an item's token after which a page of results should + // begin. Used to retrieve the next page of results after this item. + StartingAfter param.Field[string] `query:"starting_after"` } // URLQuery serializes [AuthRuleListParams]'s query parameters as `url.Values`. diff --git a/authrule_test.go b/authrule_test.go index 062e964..76dd587 100644 --- a/authrule_test.go +++ b/authrule_test.go @@ -109,8 +109,9 @@ func TestAuthRuleListWithOptionalParams(t *testing.T) { option.WithAPIKey("APIKey"), ) _, err := client.AuthRules.List(context.TODO(), lithic.AuthRuleListParams{ - Page: lithic.F(int64(0)), - PageSize: lithic.F(int64(1)), + EndingBefore: lithic.F("string"), + PageSize: lithic.F(int64(1)), + StartingAfter: lithic.F("string"), }) if err != nil { var apierr *lithic.Error diff --git a/card.go b/card.go index b387b46..d952139 100644 --- a/card.go +++ b/card.go @@ -67,7 +67,7 @@ func (r *CardService) Update(ctx context.Context, cardToken string, body CardUpd } // List cards. -func (r *CardService) List(ctx context.Context, query CardListParams, opts ...option.RequestOption) (res *shared.Page[Card], err error) { +func (r *CardService) List(ctx context.Context, query CardListParams, opts ...option.RequestOption) (res *shared.CursorPage[Card], err error) { var raw *http.Response opts = append(r.Options, opts...) opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) @@ -85,8 +85,8 @@ func (r *CardService) List(ctx context.Context, query CardListParams, opts ...op } // List cards. -func (r *CardService) ListAutoPaging(ctx context.Context, query CardListParams, opts ...option.RequestOption) *shared.PageAutoPager[Card] { - return shared.NewPageAutoPager(r.List(ctx, query, opts...)) +func (r *CardService) ListAutoPaging(ctx context.Context, query CardListParams, opts ...option.RequestOption) *shared.CursorPageAutoPager[Card] { + return shared.NewCursorPageAutoPager(r.List(ctx, query, opts...)) } // Handling full card PANs and CVV codes requires that you comply with the Payment @@ -720,10 +720,14 @@ type CardListParams struct { // Date string in RFC 3339 format. Only entries created before the specified date // will be included. UTC time zone. End param.Field[time.Time] `query:"end" format:"date-time"` - // Page (for pagination). - Page param.Field[int64] `query:"page"` + // A cursor representing an item's token before which a page of results should end. + // Used to retrieve the previous page of results before this item. + EndingBefore param.Field[string] `query:"ending_before"` // Page size (for pagination). PageSize param.Field[int64] `query:"page_size"` + // A cursor representing an item's token after which a page of results should + // begin. Used to retrieve the next page of results after this item. + StartingAfter param.Field[string] `query:"starting_after"` // Returns cards with the specified state. State param.Field[CardListParamsState] `query:"state"` } diff --git a/card_test.go b/card_test.go index 49ca143..d5dceca 100644 --- a/card_test.go +++ b/card_test.go @@ -136,12 +136,13 @@ func TestCardListWithOptionalParams(t *testing.T) { option.WithAPIKey("APIKey"), ) _, err := client.Cards.List(context.TODO(), lithic.CardListParams{ - AccountToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), - Begin: lithic.F(time.Now()), - End: lithic.F(time.Now()), - Page: lithic.F(int64(0)), - PageSize: lithic.F(int64(1)), - State: lithic.F(lithic.CardListParamsStateOpen), + AccountToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + Begin: lithic.F(time.Now()), + End: lithic.F(time.Now()), + EndingBefore: lithic.F("string"), + PageSize: lithic.F(int64(1)), + StartingAfter: lithic.F("string"), + State: lithic.F(lithic.CardListParamsStateOpen), }) if err != nil { var apierr *lithic.Error diff --git a/eventsubscription.go b/eventsubscription.go index 15541ff..0c9e6ea 100644 --- a/eventsubscription.go +++ b/eventsubscription.go @@ -125,6 +125,9 @@ func (r *EventSubscriptionService) Recover(ctx context.Context, eventSubscriptio // Replays messages to the endpoint. Only messages that were created after `begin` // will be sent. Messages that were previously sent to the endpoint are not resent. +// Message will be retried if endpoint responds with a non-2xx status code. See +// [Retry Schedule](https://docs.lithic.com/docs/events-api#retry-schedule) for +// details. func (r *EventSubscriptionService) ReplayMissing(ctx context.Context, eventSubscriptionToken string, body EventSubscriptionReplayMissingParams, opts ...option.RequestOption) (err error) { opts = append(r.Options[:], opts...) opts = append([]option.RequestOption{option.WithHeader("Accept", "")}, opts...) diff --git a/internal/requestconfig/requestconfig.go b/internal/requestconfig/requestconfig.go index 6139780..140225b 100644 --- a/internal/requestconfig/requestconfig.go +++ b/internal/requestconfig/requestconfig.go @@ -100,7 +100,7 @@ func NewRequestConfig(ctx context.Context, method string, u string, body interfa } req.Header.Set("Idempotency-Key", "stainless-go-"+uuid.New().String()) req.Header.Set("Accept", "application/json") - + req.Header.Set("X-Lithic-Pagination", "cursor") for k, v := range getPlatformProperties() { req.Header.Add(k, v) } diff --git a/internal/shared/pagination.go b/internal/shared/pagination.go index f9a376d..fa95cf4 100644 --- a/internal/shared/pagination.go +++ b/internal/shared/pagination.go @@ -3,8 +3,6 @@ package shared import ( - "context" - "fmt" "net/http" "reflect" @@ -13,105 +11,6 @@ import ( "github.com/lithic-com/lithic-go/option" ) -type Page[T any] struct { - Data []T `json:"data,required"` - // Page number. - Page int64 `json:"page,required"` - // Total number of entries. - TotalEntries int64 `json:"total_entries,required"` - // Total number of pages. - TotalPages int64 `json:"total_pages,required"` - JSON pageJSON - cfg *requestconfig.RequestConfig - res *http.Response -} - -// pageJSON contains the JSON metadata for the struct [Page[T]] -type pageJSON struct { - Data apijson.Field - Page apijson.Field - TotalEntries apijson.Field - TotalPages apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *Page[T]) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -// NextPage returns the next page as defined by this pagination style. When there -// is no next page, this function will return a 'nil' for the page value, but will -// not return an error -func (r *Page[T]) GetNextPage() (res *Page[T], err error) { - currentPage := r.Page - if currentPage >= r.TotalPages { - return nil, nil - } - cfg := r.cfg.Clone(context.Background()) - query := cfg.Request.URL.Query() - query.Set("page", fmt.Sprintf("%d", currentPage+1)) - cfg.Request.URL.RawQuery = query.Encode() - var raw *http.Response - cfg.ResponseInto = &raw - cfg.ResponseBodyInto = &res - err = cfg.Execute() - if err != nil { - return nil, err - } - res.SetPageConfig(cfg, raw) - return res, nil -} - -func (r *Page[T]) SetPageConfig(cfg *requestconfig.RequestConfig, res *http.Response) { - r.cfg = cfg - r.res = res -} - -type PageAutoPager[T any] struct { - page *Page[T] - cur T - idx int - run int - err error -} - -func NewPageAutoPager[T any](page *Page[T], err error) *PageAutoPager[T] { - return &PageAutoPager[T]{ - page: page, - err: err, - } -} - -func (r *PageAutoPager[T]) Next() bool { - if r.page == nil || len(r.page.Data) == 0 { - return false - } - if r.idx >= len(r.page.Data) { - r.idx = 0 - r.page, r.err = r.page.GetNextPage() - if r.err != nil || r.page == nil || len(r.page.Data) == 0 { - return false - } - } - r.cur = r.page.Data[r.idx] - r.run += 1 - r.idx += 1 - return true -} - -func (r *PageAutoPager[T]) Current() T { - return r.cur -} - -func (r *PageAutoPager[T]) Err() error { - return r.err -} - -func (r *PageAutoPager[T]) Index() int { - return r.run -} - type CursorPage[T any] struct { Data []T `json:"data,required"` HasMore bool `json:"has_more,required"` diff --git a/transaction.go b/transaction.go index 395c5c2..684060e 100644 --- a/transaction.go +++ b/transaction.go @@ -44,7 +44,7 @@ func (r *TransactionService) Get(ctx context.Context, transactionToken string, o } // List card transactions. -func (r *TransactionService) List(ctx context.Context, query TransactionListParams, opts ...option.RequestOption) (res *shared.Page[Transaction], err error) { +func (r *TransactionService) List(ctx context.Context, query TransactionListParams, opts ...option.RequestOption) (res *shared.CursorPage[Transaction], err error) { var raw *http.Response opts = append(r.Options, opts...) opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) @@ -62,8 +62,8 @@ func (r *TransactionService) List(ctx context.Context, query TransactionListPara } // List card transactions. -func (r *TransactionService) ListAutoPaging(ctx context.Context, query TransactionListParams, opts ...option.RequestOption) *shared.PageAutoPager[Transaction] { - return shared.NewPageAutoPager(r.List(ctx, query, opts...)) +func (r *TransactionService) ListAutoPaging(ctx context.Context, query TransactionListParams, opts ...option.RequestOption) *shared.CursorPageAutoPager[Transaction] { + return shared.NewCursorPageAutoPager(r.List(ctx, query, opts...)) } // Simulates an authorization request from the payment network as if it came from a @@ -943,13 +943,17 @@ type TransactionListParams struct { // Date string in RFC 3339 format. Only entries created before the specified date // will be included. UTC time zone. End param.Field[time.Time] `query:"end" format:"date-time"` - // Page (for pagination). - Page param.Field[int64] `query:"page"` + // A cursor representing an item's token before which a page of results should end. + // Used to retrieve the previous page of results before this item. + EndingBefore param.Field[string] `query:"ending_before"` // Page size (for pagination). PageSize param.Field[int64] `query:"page_size"` // Filters for transactions using transaction result field. Can filter by // `APPROVED`, and `DECLINED`. Result param.Field[TransactionListParamsResult] `query:"result"` + // A cursor representing an item's token after which a page of results should + // begin. Used to retrieve the next page of results after this item. + StartingAfter param.Field[string] `query:"starting_after"` } // URLQuery serializes [TransactionListParams]'s query parameters as `url.Values`. diff --git a/transaction_test.go b/transaction_test.go index 272b009..82b6d48 100644 --- a/transaction_test.go +++ b/transaction_test.go @@ -49,13 +49,14 @@ func TestTransactionListWithOptionalParams(t *testing.T) { option.WithAPIKey("APIKey"), ) _, err := client.Transactions.List(context.TODO(), lithic.TransactionListParams{ - AccountToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), - Begin: lithic.F(time.Now()), - CardToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), - End: lithic.F(time.Now()), - Page: lithic.F(int64(0)), - PageSize: lithic.F(int64(1)), - Result: lithic.F(lithic.TransactionListParamsResultApproved), + AccountToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + Begin: lithic.F(time.Now()), + CardToken: lithic.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + End: lithic.F(time.Now()), + EndingBefore: lithic.F("string"), + PageSize: lithic.F(int64(1)), + Result: lithic.F(lithic.TransactionListParamsResultApproved), + StartingAfter: lithic.F("string"), }) if err != nil { var apierr *lithic.Error