Skip to content

Commit

Permalink
api: implement pagination and query params
Browse files Browse the repository at this point in the history
* add new endpoints, all of them include a `pagination` field in reply, and accept QueryParams:
  * GET /elections
    * page
    * limit
    * status
    * organizationId
    * electionId
    * withResults
    * finalResults
    * manuallyEnded

  * GET /accounts
    * page
    * limit

  * GET /chain/transactions
    * page
    * limit
    * height
    * type

  * GET /chain/organizations
    * page
    * limit
    * organizationId

  * GET /chain/fees
    * page
    * limit
    * reference
    * type
    * accountId

  * GET /votes
    * page
    * limit
    * electionId

* mark all of these endpoints as deprecated on swagger docs:
  * GET /accounts/page/{page}
  * GET /accounts/{organizationID}/elections/status/{status}/page/{page}
  * GET /accounts/{organizationID}/elections/page/{page}
  * GET /elections/page/{page}
  * POST /elections/filter/page/{page}
  * GET /chain/organizations/page/{page}
  * POST /chain/organizations/filter/page/{page}
  * GET /elections/{electionId}/votes/page/{page}
all of these were anyway refactored in a backwards-compatible manner,
to unify pagination logic (adding `pagination` field)

* api: return ErrPageNotFound on all paginated endpoints, when page is negative or higher than last_page
* api: new param `limit` in all paginated endpoints
  (defaults to DefaultItemsPerPage if ommitted, and can't surpass hardcoded MaxItemsPerPage)
* api: all paginated endpoints return an empty list in case of no results (instead of 404)

api internal code refactorings:
* api: unify hardcoded structs into a new types:
  * AccountsList
  * ElectionsList
  * OrganizationsList
  * FeesList
  * VotesList
  * TransactionsList
  * CountResult

* api: deduplicate several code snippets, with marshalAndSend and parse* helpers
* api: rename const MaxPageSize -> MaxItemsPerPage
* api: add Param* consts, and fix case of all URLParams 'ID' -> 'Id'
* api: add *Params types (PaginationParams, ElectionParams, etc)

* api: fix strings in errors returned to client, replacing "ID" -> "Id"
* api: fix swagger docs, replace many occurences of "ID" -> "Id"
* api: fix swagger docs, lots of small inaccuracies

* test: add TestAPIAccountsList and TestAPIElectionsList

indexer changes:
* rename GetListAccounts -> AccountList
* rename GetEnvelopes -> VoteList
* replace GetTokenFees* methods with a single TokenFeesList
* methods AccountList, ProcessList, EntityList, VoteList, TokenFeesList now:
  * return a TotalCount
  * reordered and renamed args (from, max) -> (limit, offset)
  • Loading branch information
altergui committed Jul 26, 2024
1 parent fa0c9ba commit 64c39dc
Show file tree
Hide file tree
Showing 33 changed files with 2,061 additions and 1,391 deletions.
367 changes: 185 additions & 182 deletions api/accounts.go

Large diffs are not rendered by default.

30 changes: 28 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,34 @@ import (

// @securityDefinitions.basic BasicAuth

// MaxPageSize defines the maximum number of results returned by the paginated endpoints
const MaxPageSize = 10
const (
// DefaultItemsPerPage defines how many items per page are returned by the paginated endpoints,
// when the client doesn't specify a `limit` param
DefaultItemsPerPage = 10
// MaxItemsPerPage defines a ceiling for the `limit` param passed by the client
MaxItemsPerPage = 100
)

// These consts define the keywords for query (?param=), url (/url/param/) and POST params.
// Note: In JS/TS acronyms like "ID" are camelCased as in "Id".
//
//nolint:revive
const (
ParamAccountId = "accountId"
ParamCensusId = "censusId"
ParamElectionId = "electionId"
ParamOrganizationId = "organizationId"
ParamVoteId = "voteId"
ParamPage = "page"
ParamLimit = "limit"
ParamStatus = "status"
ParamWithResults = "withResults"
ParamFinalResults = "finalResults"
ParamManuallyEnded = "manuallyEnded"
ParamHeight = "height"
ParamReference = "reference"
ParamType = "type"
)

var (
ErrMissingModulesForHandler = fmt.Errorf("missing modules attached for enabling handler")
Expand Down
124 changes: 101 additions & 23 deletions api/api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,84 @@ import (
"google.golang.org/protobuf/encoding/protojson"
)

type Organization struct {
OrganizationID types.HexBytes `json:"organizationID,omitempty" `
Elections []*ElectionSummary `json:"elections,omitempty"`
Organizations []*OrganizationList `json:"organizations,omitempty"`
Count *uint64 `json:"count,omitempty" example:"1"`
// ### Params accepted ###

// PaginationParams allows the client to request a specific page, and how many items per page
type PaginationParams struct {
Page int `json:"page,omitempty"`
Limit int `json:"limit,omitempty"`
}

// ElectionParams allows the client to filter elections
type ElectionParams struct {
PaginationParams
OrganizationID string `json:"organizationId,omitempty"`
ElectionID string `json:"electionId,omitempty"`
Status string `json:"status,omitempty"`
WithResults *bool `json:"withResults,omitempty"`
FinalResults *bool `json:"finalResults,omitempty"`
ManuallyEnded *bool `json:"manuallyEnded,omitempty"`
}

// OrganizationParams allows the client to filter organizations
type OrganizationParams struct {
PaginationParams
OrganizationID string `json:"organizationId,omitempty"`
}

// AccountParams allows the client to filter accounts
type AccountParams struct {
PaginationParams
}

// TransactionParams allows the client to filter transactions
type TransactionParams struct {
PaginationParams
Height uint64 `json:"height,omitempty"`
Type string `json:"type,omitempty"`
}

// FeesParams allows the client to filter fees
type FeesParams struct {
PaginationParams
Reference string `json:"reference,omitempty"`
Type string `json:"type,omitempty"`
AccountID string `json:"accountId,omitempty"`
}

// VoteParams allows the client to filter votes
type VoteParams struct {
PaginationParams
ElectionID string `json:"electionId,omitempty"`
}

// ### Objects returned ###

// CountResult wraps a count inside an object
type CountResult struct {
Count uint64 `json:"count" example:"10"`
}

// Pagination contains all the values needed for the UI to easily organize the returned data
type Pagination struct {
TotalItems uint64 `json:"totalItems"`
PreviousPage *uint64 `json:"previousPage"`
CurrentPage uint64 `json:"currentPage"`
NextPage *uint64 `json:"nextPage"`
LastPage uint64 `json:"lastPage"`
}

type OrganizationList struct {
type OrganizationSummary struct {
OrganizationID types.HexBytes `json:"organizationID" example:"0x370372b92514d81a0e3efb8eba9d036ae0877653"`
ElectionCount uint64 `json:"electionCount" example:"1"`
}

// OrganizationsList is used to return a paginated list to the client
type OrganizationsList struct {
Organizations []*OrganizationSummary `json:"organizations"`
Pagination *Pagination `json:"pagination"`
}

type ElectionSummary struct {
ElectionID types.HexBytes `json:"electionId" `
OrganizationID types.HexBytes `json:"organizationId" `
Expand All @@ -37,6 +103,12 @@ type ElectionSummary struct {
ChainID string `json:"chainId"`
}

// ElectionsList is used to return a paginated list to the client
type ElectionsList struct {
Elections []*ElectionSummary `json:"elections"`
Pagination *Pagination `json:"pagination"`
}

// ElectionResults is the struct used to wrap the results of an election
type ElectionResults struct {
// ABIEncoded is the abi encoded election results
Expand Down Expand Up @@ -100,13 +172,6 @@ type ElectionDescription struct {
TempSIKs bool `json:"tempSIKs"`
}

type ElectionFilter struct {
OrganizationID types.HexBytes `json:"organizationId,omitempty" `
ElectionID types.HexBytes `json:"electionId,omitempty" `
WithResults *bool `json:"withResults,omitempty"`
Status string `json:"status,omitempty"`
}

type Key struct {
Index int `json:"index"`
Key types.HexBytes `json:"key" `
Expand All @@ -115,7 +180,9 @@ type Key struct {
type Vote struct {
TxPayload []byte `json:"txPayload,omitempty" extensions:"x-omitempty" swaggerignore:"true"`
TxHash types.HexBytes `json:"txHash,omitempty" extensions:"x-omitempty" `
VoteID types.HexBytes `json:"voteID,omitempty" extensions:"x-omitempty" `
// VoteID here produces a `voteID` over JSON that differs in casing from the rest of params and JSONs
// but is kept for backwards compatibility
VoteID types.HexBytes `json:"voteID,omitempty" extensions:"x-omitempty" `
// Sent only for encrypted elections (no results until the end)
EncryptionKeyIndexes []uint32 `json:"encryptionKeys,omitempty" extensions:"x-omitempty"`
// For encrypted elections this will be codified
Expand All @@ -131,6 +198,11 @@ type Vote struct {
Date *time.Time `json:"date,omitempty" extensions:"x-omitempty"`
}

type VotesList struct {
Votes []*Vote `json:"votes"`
Pagination *Pagination `json:"pagination"`
}

type CensusTypeDescription struct {
Type string `json:"type"`
Size uint64 `json:"size"`
Expand Down Expand Up @@ -180,17 +252,16 @@ type TransactionReference struct {
Index uint32 `json:"transactionIndex"`
}

type TransactionMetadata struct {
Type string `json:"transactionType"`
Number uint32 `json:"transactionNumber"`
Index int32 `json:"transactionIndex"`
Hash types.HexBytes `json:"transactionHash" `
// TransactionsList is used to return a paginated list to the client
type TransactionsList struct {
Transactions []*indexertypes.Transaction `json:"transactions"`
Pagination *Pagination `json:"pagination"`
}

type BlockTransactionsInfo struct {
BlockNumber uint64 `json:"blockNumber"`
TransactionsCount uint32 `json:"transactionCount"`
Transactions []TransactionMetadata `json:"transactions"`
// FeesList is used to return a paginated list to the client
type FeesList struct {
Fees []*indexertypes.TokenFeeMeta `json:"fees"`
Pagination *Pagination `json:"pagination"`
}

type GenericTransactionWithInfo struct {
Expand Down Expand Up @@ -229,6 +300,11 @@ type Account struct {
SIK types.HexBytes `json:"sik"`
}

type AccountsList struct {
Accounts []*indexertypes.Account `json:"accounts"`
Pagination *Pagination `json:"pagination"`
}

type AccountSet struct {
TxPayload []byte `json:"txPayload,omitempty" swaggerignore:"true"`
Metadata []byte `json:"metadata,omitempty" swaggerignore:"true"`
Expand All @@ -237,6 +313,8 @@ type AccountSet struct {
}

type Census struct {
// CensusID here produces a `censusID` over JSON that differs in casing from the rest of params and JSONs
// but is kept for backwards compatibility
CensusID types.HexBytes `json:"censusID,omitempty"`
Type string `json:"type,omitempty"`
Weight *types.BigInt `json:"weight,omitempty"`
Expand Down
Loading

0 comments on commit 64c39dc

Please sign in to comment.