diff --git a/api/api_types.go b/api/api_types.go index e3b3ddd1c..d948b7207 100644 --- a/api/api_types.go +++ b/api/api_types.go @@ -23,12 +23,16 @@ type PaginationParams struct { // 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"` + 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"` + StartDateAfter *time.Time `json:"startDateAfter,omitempty"` + StartDateBefore *time.Time `json:"startDateBefore,omitempty"` + EndDateAfter *time.Time `json:"endDateAfter,omitempty"` + EndDateBefore *time.Time `json:"endDateBefore,omitempty"` } // OrganizationParams allows the client to filter organizations diff --git a/api/elections.go b/api/elections.go index 136421cdc..a3c19851c 100644 --- a/api/elections.go +++ b/api/elections.go @@ -760,7 +760,9 @@ func (a *API) buildElectionIDHandler(msg *apirest.APIdata, ctx *httprouter.HTTPC // parseElectionParams returns an ElectionParams filled with the passed params func parseElectionParams(paramPage, paramLimit, paramStatus, paramOrganizationID, paramElectionID, - paramWithResults, paramFinalResults, paramManuallyEnded string, + paramWithResults, paramFinalResults, paramManuallyEnded, + paramStartDateAfter, paramStartDateBefore, + paramEndDateAfter, paramEndDateBefore string, ) (*ElectionParams, error) { pagination, err := parsePaginationParams(paramPage, paramLimit) if err != nil { @@ -782,6 +784,11 @@ func parseElectionParams(paramPage, paramLimit, paramStatus, return nil, err } + startDateAfter, err := parseDate(paramStartDateAfter) + if err != nil { + return nil, err + } + return &ElectionParams{ PaginationParams: pagination, OrganizationID: util.TrimHex(paramOrganizationID), @@ -790,5 +797,6 @@ func parseElectionParams(paramPage, paramLimit, paramStatus, WithResults: withResults, FinalResults: finalResults, ManuallyEnded: manuallyEnded, + StartDateAfter: startDateAfter, }, nil } diff --git a/api/errors.go b/api/errors.go index 3d4e4c31b..f6ed2af3c 100644 --- a/api/errors.go +++ b/api/errors.go @@ -83,6 +83,7 @@ var ( ErrCantParseBoolean = apirest.APIerror{Code: 4055, HTTPstatus: apirest.HTTPstatusBadRequest, Err: fmt.Errorf("cannot parse string into boolean")} ErrCantParseHexString = apirest.APIerror{Code: 4056, HTTPstatus: apirest.HTTPstatusBadRequest, Err: fmt.Errorf("cannot parse string into hex bytes")} ErrPageNotFound = apirest.APIerror{Code: 4057, HTTPstatus: apirest.HTTPstatusNotFound, Err: fmt.Errorf("page not found")} + ErrCantParseDate = apirest.APIerror{Code: 4058, HTTPstatus: apirest.HTTPstatusBadRequest, Err: fmt.Errorf("cannot parse date")} ErrVochainEmptyReply = apirest.APIerror{Code: 5000, HTTPstatus: apirest.HTTPstatusInternalErr, Err: fmt.Errorf("vochain returned an empty reply")} ErrVochainSendTxFailed = apirest.APIerror{Code: 5001, HTTPstatus: apirest.HTTPstatusInternalErr, Err: fmt.Errorf("vochain SendTx failed")} ErrVochainGetTxFailed = apirest.APIerror{Code: 5002, HTTPstatus: apirest.HTTPstatusInternalErr, Err: fmt.Errorf("vochain GetTx failed")} diff --git a/api/helpers.go b/api/helpers.go index b261cb901..4749fff14 100644 --- a/api/helpers.go +++ b/api/helpers.go @@ -9,6 +9,7 @@ import ( "math/big" "strconv" "strings" + "time" cometpool "github.com/cometbft/cometbft/mempool" cometcoretypes "github.com/cometbft/cometbft/rpc/core/types" @@ -275,6 +276,20 @@ func parseBool(s string) (*bool, error) { return &b, nil } +// parseDate parses an RFC3339 string into a time.Time value. +// +// The empty string "" is treated specially, returns a nil pointer with no error. +func parseDate(s string) (*time.Time, error) { + if s == "" { + return nil, nil + } + b, err := time.Parse(time.RFC3339, s) + if err != nil { + return nil, ErrCantParseDate.With(s) + } + return &b, nil +} + // parsePaginationParams returns a PaginationParams filled with the passed params func parsePaginationParams(paramPage, paramLimit string) (PaginationParams, error) { page, err := parsePage(paramPage) diff --git a/vochain/indexer/db/processes.sql.go b/vochain/indexer/db/processes.sql.go index e05e63255..67235f482 100644 --- a/vochain/indexer/db/processes.sql.go +++ b/vochain/indexer/db/processes.sql.go @@ -304,6 +304,12 @@ WITH results AS ( OR (?10 = 1 AND manually_ended = TRUE) OR (?10 = 0 AND manually_ended = FALSE) ) + AND ( + (?11 IS NULL OR start_date >= ?11) + AND (?12 IS NULL OR start_date <= ?12) + AND (?13 IS NULL OR end_date >= ?13) + AND (?14 IS NULL OR end_date <= ?14) + ) ) ) SELECT id, total_count @@ -324,6 +330,10 @@ type SearchProcessesParams struct { HaveResults interface{} FinalResults interface{} ManuallyEnded interface{} + StartDateAfter interface{} + StartDateBefore interface{} + EndDateAfter interface{} + EndDateBefore interface{} } type SearchProcessesRow struct { @@ -343,6 +353,10 @@ func (q *Queries) SearchProcesses(ctx context.Context, arg SearchProcessesParams arg.HaveResults, arg.FinalResults, arg.ManuallyEnded, + arg.StartDateAfter, + arg.StartDateBefore, + arg.EndDateAfter, + arg.EndDateBefore, ) if err != nil { return nil, err diff --git a/vochain/indexer/queries/processes.sql b/vochain/indexer/queries/processes.sql index 4fdb80da2..aa3af9fd1 100644 --- a/vochain/indexer/queries/processes.sql +++ b/vochain/indexer/queries/processes.sql @@ -68,6 +68,12 @@ WITH results AS ( OR (sqlc.arg(manually_ended) = 1 AND manually_ended = TRUE) OR (sqlc.arg(manually_ended) = 0 AND manually_ended = FALSE) ) + AND ( + (sqlc.arg(start_date_after) IS NULL OR start_date >= sqlc.arg(start_date_after)) + AND (sqlc.arg(start_date_before) IS NULL OR start_date <= sqlc.arg(start_date_before)) + AND (sqlc.arg(end_date_after) IS NULL OR end_date >= sqlc.arg(end_date_after)) + AND (sqlc.arg(end_date_before) IS NULL OR end_date <= sqlc.arg(end_date_before)) + ) ) ) SELECT id, total_count