diff --git a/backend/pkg/api/data_access/dummy.go b/backend/pkg/api/data_access/dummy.go index a2ed4265b..18259c8c5 100644 --- a/backend/pkg/api/data_access/dummy.go +++ b/backend/pkg/api/data_access/dummy.go @@ -282,7 +282,7 @@ func (d *DummyService) AddValidatorDashboardValidatorsByGraffiti(ctx context.Con return getDummyData[[]t.VDBPostValidatorsData](ctx) } -func (d *DummyService) GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor t.ValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBManageValidatorsTableRow](ctx) } @@ -314,7 +314,7 @@ func (d *DummyService) GetValidatorDashboardSlotViz(ctx context.Context, dashboa return r.Epochs, err } -func (d *DummyService) GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search t.VDBSummarySearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBSummaryTableRow](ctx) } func (d *DummyService) GetValidatorDashboardGroupSummary(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, protocolModes t.VDBProtocolModes) (*t.VDBGroupSummaryData, error) { @@ -338,7 +338,7 @@ func (d *DummyService) GetValidatorDashboardProposalSummaryValidators(ctx contex return getDummyStruct[t.VDBProposalSummaryValidators](ctx) } -func (d *DummyService) GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor t.RewardsCursor, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBRewardsTableRow](ctx) } @@ -350,11 +350,11 @@ func (d *DummyService) GetValidatorDashboardRewardsChart(ctx context.Context, da return getDummyStruct[t.ChartData[int, decimal.Decimal]](ctx) } -func (d *DummyService) GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor string, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor t.ValidatorDutiesCursor, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBEpochDutiesTableRow](ctx) } -func (d *DummyService) GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBBlocksColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor t.BlocksCursor, colSort t.Sort[enums.VDBBlocksColumn], search t.VDBBlocksSearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBBlocksTableRow](ctx) } @@ -366,11 +366,11 @@ func (d *DummyService) GetValidatorDashboardGroupHeatmap(ctx context.Context, da return getDummyStruct[t.VDBHeatmapTooltipData](ctx) } -func (d *DummyService) GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.ELDepositsCursor, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBExecutionDepositsTableRow](ctx) } -func (d *DummyService) GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.CLDepositsCursor, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBConsensusDepositsTableRow](ctx) } @@ -382,7 +382,7 @@ func (d *DummyService) GetValidatorDashboardTotalClDeposits(ctx context.Context, return getDummyStruct[t.VDBTotalConsensusDepositsData](ctx) } -func (d *DummyService) GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor t.WithdrawalsCursor, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) { return []t.VDBWithdrawalsTableRow{}, &t.Paging{}, nil } @@ -390,7 +390,7 @@ func (d *DummyService) GetValidatorDashboardTotalWithdrawals(ctx context.Context return getDummyStruct[t.VDBTotalWithdrawalsData](ctx) } -func (d *DummyService) GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor t.VDBRocketPoolCursor, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBRocketPoolTableRow](ctx) } @@ -398,7 +398,7 @@ func (d *DummyService) GetValidatorDashboardTotalRocketPool(ctx context.Context, return getDummyStruct[t.VDBRocketPoolTableRow](ctx) } -func (d *DummyService) GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node string, cursor string, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node string, cursor t.VDBRocketPoolMinipoolsCursor, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) { return getDummyWithPaging[t.VDBRocketPoolMinipoolsTableRow](ctx) } @@ -549,7 +549,7 @@ func (d *DummyService) GetValidatorDashboardPublicIdCount(ctx context.Context, d func (d *DummyService) GetNotificationOverview(ctx context.Context, userId uint64) (*t.NotificationOverviewData, error) { return getDummyStruct[t.NotificationOverviewData](ctx) } -func (d *DummyService) GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor string, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) { +func (d *DummyService) GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor t.NotificationsDashboardsCursor, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) { return getDummyWithPaging[t.NotificationDashboardsTableRow](ctx) } @@ -561,13 +561,14 @@ func (d *DummyService) GetAccountDashboardNotificationDetails(ctx context.Contex return getDummyStruct[t.NotificationAccountDashboardDetail](ctx) } -func (d *DummyService) GetMachineNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) { +func (d *DummyService) GetMachineNotifications(ctx context.Context, userId uint64, cursor t.NotificationMachinesCursor, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) { return getDummyWithPaging[t.NotificationMachinesTableRow](ctx) } -func (d *DummyService) GetClientNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) { +func (d *DummyService) GetClientNotifications(ctx context.Context, userId uint64, cursor t.NotificationClientsCursor, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) { return getDummyWithPaging[t.NotificationClientsTableRow](ctx) } -func (d *DummyService) GetNetworkNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) { + +func (d *DummyService) GetNetworkNotifications(ctx context.Context, userId uint64, cursor t.NotificationNetworksCursor, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) { return getDummyWithPaging[t.NotificationNetworksTableRow](ctx) } @@ -594,7 +595,7 @@ func (d *DummyService) UpdateNotificationSettingsClients(ctx context.Context, us return getDummyStruct[t.NotificationSettingsClient](ctx) } -func (d *DummyService) GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) { +func (d *DummyService) GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor t.NotificationSettingsCursor, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) { r, p, err := getDummyWithPaging[t.NotificationSettingsDashboardsTableRow](ctx) for i, n := range r { var settings interface{} @@ -784,7 +785,7 @@ func (d *DummyService) PostUserMachineMetrics(ctx context.Context, userID uint64 return nil } -func (d *DummyService) GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) { +func (d *DummyService) GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor t.VDBMobileValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) { return getDummyWithPaging[t.MobileValidatorDashboardValidatorsTableRow](ctx) } diff --git a/backend/pkg/api/data_access/mobile.go b/backend/pkg/api/data_access/mobile.go index 89b2b7b03..e2042b950 100644 --- a/backend/pkg/api/data_access/mobile.go +++ b/backend/pkg/api/data_access/mobile.go @@ -30,7 +30,7 @@ type AppRepository interface { AddMobilePurchase(ctx context.Context, tx *sql.Tx, userID uint64, paymentDetails t.MobileSubscription, verifyResponse *userservice.VerifyResponse, extSubscriptionId string) error GetLatestBundleForNativeVersion(ctx context.Context, nativeVersion uint64) (*t.MobileAppBundleStats, error) IncrementBundleDeliveryCount(ctx context.Context, bundleVerison uint64) error - GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) + GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor t.VDBMobileValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) } // GetUserIdByRefreshToken basically used to confirm the claimed user id with the refresh token. Returns the userId if successful @@ -366,8 +366,8 @@ func (d *DataAccessService) getInternalRpNetworkStats(ctx context.Context) (*t.R return &networkStats, err } -func (d *DataAccessService) GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) { - result, p, err := d.GetValidatorDashboardValidators(ctx, dashboardId, groupId, cursor, colSort, search, limit) +func (d *DataAccessService) GetValidatorDashboardMobileValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, cursor t.VDBMobileValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.MobileValidatorDashboardValidatorsTableRow, *t.Paging, error) { + result, p, err := d.GetValidatorDashboardValidators(ctx, dashboardId, groupId, cursor.ValidatorsCursor, colSort, search, limit) if err != nil { return nil, p, err } diff --git a/backend/pkg/api/data_access/notifications.go b/backend/pkg/api/data_access/notifications.go index 40115691d..b378a8b65 100644 --- a/backend/pkg/api/data_access/notifications.go +++ b/backend/pkg/api/data_access/notifications.go @@ -37,14 +37,14 @@ import ( type NotificationsRepository interface { GetNotificationOverview(ctx context.Context, userId uint64) (*t.NotificationOverviewData, error) - GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor string, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) + GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor t.NotificationsDashboardsCursor, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) // depending on how notifications are implemented, we may need to use something other than `notificationId` for identifying the notification GetValidatorDashboardNotificationDetails(ctx context.Context, dashboardId t.VDBIdPrimary, groupId uint64, epoch uint64, search string) (*t.NotificationValidatorDashboardDetail, error) GetAccountDashboardNotificationDetails(ctx context.Context, dashboardId uint64, groupId uint64, epoch uint64, search string) (*t.NotificationAccountDashboardDetail, error) - GetMachineNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) - GetClientNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) - GetNetworkNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) + GetMachineNotifications(ctx context.Context, userId uint64, cursor t.NotificationMachinesCursor, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) + GetClientNotifications(ctx context.Context, userId uint64, cursor t.NotificationClientsCursor, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) + GetNetworkNotifications(ctx context.Context, userId uint64, cursor t.NotificationNetworksCursor, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) GetNotificationSettings(ctx context.Context, userId uint64) (*t.NotificationSettings, error) GetNotificationSettingsDefaultValues(ctx context.Context) (*t.NotificationSettingsDefaultValues, error) @@ -54,7 +54,7 @@ type NotificationsRepository interface { UpdateNotificationSettingsPairedDevice(ctx context.Context, pairedDeviceId uint64, name string, IsNotificationsEnabled bool) error DeleteNotificationSettingsPairedDevice(ctx context.Context, pairedDeviceId uint64) error UpdateNotificationSettingsClients(ctx context.Context, userId uint64, clientId uint64, IsSubscribed bool) (*t.NotificationSettingsClient, error) - GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) + GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor t.NotificationSettingsCursor, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) UpdateNotificationSettingsValidatorDashboard(ctx context.Context, userId uint64, dashboardId t.VDBIdPrimary, groupId uint64, settings t.NotificationSettingsValidatorDashboard) error UpdateNotificationSettingsAccountDashboard(ctx context.Context, userId uint64, dashboardId t.VDBIdPrimary, groupId uint64, settings t.NotificationSettingsAccountDashboard) error @@ -260,16 +260,8 @@ func (d *DataAccessService) GetNotificationOverview(ctx context.Context, userId return &response, err } -func (d *DataAccessService) GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor string, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetDashboardNotifications(ctx context.Context, userId uint64, chainIds []uint64, cursor t.NotificationsDashboardsCursor, colSort t.Sort[enums.NotificationDashboardsColumn], search string, limit uint64) ([]t.NotificationDashboardsTableRow, *t.Paging, error) { response := []t.NotificationDashboardsTableRow{} - var err error - - var currentCursor t.NotificationsDashboardsCursor - if cursor != "" { - if currentCursor, err = utils.StringToCursor[t.NotificationsDashboardsCursor](cursor); err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as NotificationsDashboardsCursor: %w", err) - } - } // validator query vdbQuery := goqu.Dialect("postgres"). @@ -346,14 +338,14 @@ func (d *DataAccessService) GetDashboardNotifications(ctx context.Context, userI // sorting defaultColumns := []t.SortColumn{ - {Column: enums.NotificationsDashboardsColumns.Timestamp.ToExpr(), Desc: true, Offset: currentCursor.Epoch}, - {Column: enums.NotificationsDashboardsColumns.DashboardName.ToExpr(), Desc: false, Offset: currentCursor.DashboardName}, - {Column: enums.NotificationsDashboardsColumns.DashboardId.ToExpr(), Desc: false, Offset: currentCursor.DashboardId}, - {Column: enums.NotificationsDashboardsColumns.GroupName.ToExpr(), Desc: false, Offset: currentCursor.GroupName}, - {Column: enums.NotificationsDashboardsColumns.GroupId.ToExpr(), Desc: false, Offset: currentCursor.GroupId}, - {Column: enums.NotificationsDashboardsColumns.ChainId.ToExpr(), Desc: true, Offset: currentCursor.ChainId}, - } - order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, currentCursor.GenericCursor) + {Column: enums.NotificationsDashboardsColumns.Timestamp.ToExpr(), Desc: true, Offset: cursor.Epoch}, + {Column: enums.NotificationsDashboardsColumns.DashboardName.ToExpr(), Desc: false, Offset: cursor.DashboardName}, + {Column: enums.NotificationsDashboardsColumns.DashboardId.ToExpr(), Desc: false, Offset: cursor.DashboardId}, + {Column: enums.NotificationsDashboardsColumns.GroupName.ToExpr(), Desc: false, Offset: cursor.GroupName}, + {Column: enums.NotificationsDashboardsColumns.GroupId.ToExpr(), Desc: false, Offset: cursor.GroupId}, + {Column: enums.NotificationsDashboardsColumns.ChainId.ToExpr(), Desc: true, Offset: cursor.ChainId}, + } + order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, cursor.GenericCursor) if err != nil { return nil, nil, err } @@ -387,14 +379,14 @@ func (d *DataAccessService) GetDashboardNotifications(ctx context.Context, userI if moreDataFlag { response = response[:len(response)-1] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(response) } - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return response, &t.Paging{}, nil } - paging, err := utils.GetPagingFromData(response, currentCursor, moreDataFlag) + paging, err := utils.GetPagingFromData(response, cursor, moreDataFlag) if err != nil { return nil, nil, err } @@ -699,20 +691,10 @@ func (d *DataAccessService) GetAccountDashboardNotificationDetails(ctx context.C return d.dummy.GetAccountDashboardNotificationDetails(ctx, dashboardId, groupId, epoch, search) } -func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) { +func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId uint64, cursor t.NotificationMachinesCursor, colSort t.Sort[enums.NotificationMachinesColumn], search string, limit uint64) ([]t.NotificationMachinesTableRow, *t.Paging, error) { result := make([]t.NotificationMachinesTableRow, 0) var paging t.Paging - // Initialize the cursor - var currentCursor t.NotificationMachinesCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.NotificationMachinesCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as NotificationMachinesCursor: %w", err) - } - } - // ------------------------------------- // Get the machine notification history notificationHistory := []struct { @@ -742,19 +724,19 @@ func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId // Sorting and limiting if cursor is present // Rows can be uniquely identified by (ts, machine_id, event_type) defaultColumns := []t.SortColumn{ - {Column: enums.NotificationsMachinesColumns.Timestamp.ToExpr(), Desc: true, Offset: currentCursor.Ts}, - {Column: enums.NotificationsMachinesColumns.MachineId.ToExpr(), Desc: false, Offset: currentCursor.MachineId}, - {Column: enums.NotificationsMachinesColumns.EventType.ToExpr(), Desc: false, Offset: currentCursor.EventType}, + {Column: enums.NotificationsMachinesColumns.Timestamp.ToExpr(), Desc: true, Offset: cursor.Ts}, + {Column: enums.NotificationsMachinesColumns.MachineId.ToExpr(), Desc: false, Offset: cursor.MachineId}, + {Column: enums.NotificationsMachinesColumns.EventType.ToExpr(), Desc: false, Offset: cursor.EventType}, } var offset interface{} switch colSort.Column { case enums.NotificationsMachinesColumns.MachineName: - offset = currentCursor.MachineName + offset = cursor.MachineName case enums.NotificationsMachinesColumns.Threshold: - offset = currentCursor.EventThreshold + offset = cursor.EventThreshold } - order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc, Offset: offset}, currentCursor.GenericCursor) + order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc, Offset: offset}, cursor.GenericCursor) if err != nil { return nil, nil, err } @@ -790,7 +772,7 @@ func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -801,32 +783,22 @@ func (d *DataAccessService) GetMachineNotifications(ctx context.Context, userId cursorData = cursorData[:limit] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) slices.Reverse(cursorData) } - p, err := utils.GetPagingFromData(cursorData, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(cursorData, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } return result, p, nil } -func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId uint64, cursor t.NotificationClientsCursor, colSort t.Sort[enums.NotificationClientsColumn], search string, limit uint64) ([]t.NotificationClientsTableRow, *t.Paging, error) { result := make([]t.NotificationClientsTableRow, 0) var paging t.Paging - // Initialize the cursor - var currentCursor t.NotificationClientsCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.NotificationClientsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as NotificationClientsCursor: %w", err) - } - } - // ------------------------------------- // Get the client notification history notificationHistory := []struct { @@ -854,10 +826,10 @@ func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId u // Sorting and limiting if cursor is present // Rows can be uniquely identified by (ts, client) defaultColumns := []t.SortColumn{ - {Column: enums.NotificationsClientsColumns.Timestamp.ToExpr(), Desc: true, Offset: currentCursor.Ts}, - {Column: enums.NotificationsClientsColumns.ClientName.ToExpr(), Desc: false, Offset: currentCursor.Client}, + {Column: enums.NotificationsClientsColumns.Timestamp.ToExpr(), Desc: true, Offset: cursor.Ts}, + {Column: enums.NotificationsClientsColumns.ClientName.ToExpr(), Desc: false, Offset: cursor.Client}, } - order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, currentCursor.GenericCursor) + order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, cursor.GenericCursor) if err != nil { return nil, nil, err } @@ -894,7 +866,7 @@ func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId u // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -905,12 +877,12 @@ func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId u cursorData = cursorData[:limit] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) slices.Reverse(cursorData) } - p, err := utils.GetPagingFromData(cursorData, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(cursorData, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } @@ -918,20 +890,10 @@ func (d *DataAccessService) GetClientNotifications(ctx context.Context, userId u return result, p, nil } -func (d *DataAccessService) GetNetworkNotifications(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) { +func (d *DataAccessService) GetNetworkNotifications(ctx context.Context, userId uint64, cursor t.NotificationNetworksCursor, colSort t.Sort[enums.NotificationNetworksColumn], limit uint64) ([]t.NotificationNetworksTableRow, *t.Paging, error) { result := make([]t.NotificationNetworksTableRow, 0) var paging t.Paging - // Initialize the cursor - var currentCursor t.NotificationNetworksCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.NotificationNetworksCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as NotificationNetworksCursor: %w", err) - } - } - // ------------------------------------- // Get the network notification history notificationHistory := []struct { @@ -954,11 +916,11 @@ func (d *DataAccessService) GetNetworkNotifications(ctx context.Context, userId // Sorting and limiting if cursor is present // Rows can be uniquely identified by (ts, network, event_type) defaultColumns := []t.SortColumn{ - {Column: enums.NotificationNetworksColumns.Timestamp.ToExpr(), Desc: true, Offset: currentCursor.Ts}, - {Column: enums.NotificationNetworksColumns.Network.ToExpr(), Desc: false, Offset: currentCursor.Network}, - {Column: enums.NotificationNetworksColumns.EventType.ToExpr(), Desc: false, Offset: currentCursor.EventType}, + {Column: enums.NotificationNetworksColumns.Timestamp.ToExpr(), Desc: true, Offset: cursor.Ts}, + {Column: enums.NotificationNetworksColumns.Network.ToExpr(), Desc: false, Offset: cursor.Network}, + {Column: enums.NotificationNetworksColumns.EventType.ToExpr(), Desc: false, Offset: cursor.EventType}, } - order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, currentCursor.GenericCursor) + order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc}, cursor.GenericCursor) if err != nil { return nil, nil, err } @@ -1005,7 +967,7 @@ func (d *DataAccessService) GetNetworkNotifications(ctx context.Context, userId // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -1016,12 +978,12 @@ func (d *DataAccessService) GetNetworkNotifications(ctx context.Context, userId cursorData = cursorData[:limit] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) slices.Reverse(cursorData) } - p, err := utils.GetPagingFromData(cursorData, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(cursorData, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } @@ -1557,23 +1519,13 @@ func (d *DataAccessService) UpdateNotificationSettingsClients(ctx context.Contex return result, nil } -func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor string, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Context, userId uint64, cursor t.NotificationSettingsCursor, colSort t.Sort[enums.NotificationSettingsDashboardColumn], search string, limit uint64) ([]t.NotificationSettingsDashboardsTableRow, *t.Paging, error) { result := make([]t.NotificationSettingsDashboardsTableRow, 0) var paging t.Paging wg := errgroup.Group{} - // Initialize the cursor - var currentCursor t.NotificationSettingsCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.NotificationSettingsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as NotificationSettingsCursor: %w", err) - } - } - - isReverseDirection := (colSort.Desc && !currentCursor.IsReverse()) || (!colSort.Desc && currentCursor.IsReverse()) + isReverseDirection := (colSort.Desc && !cursor.IsReverse()) || (!colSort.Desc && cursor.IsReverse()) // ------------------------------------- // Get the events @@ -1667,7 +1619,7 @@ func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Contex // return nil // }) - err = wg.Wait() + err := wg.Wait() if err != nil { return nil, nil, fmt.Errorf("error retrieving dashboard notification data: %w", err) } @@ -1879,11 +1831,11 @@ func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Contex // Paging // Find the index for the cursor and limit the data - if currentCursor.IsValid() { + if cursor.IsValid() { for idx, row := range result { - if row.DashboardId == currentCursor.DashboardId && - row.GroupId == currentCursor.GroupId && - row.IsAccountDashboard == currentCursor.IsAccountDashboard { + if row.DashboardId == cursor.DashboardId && + row.GroupId == cursor.GroupId && + row.IsAccountDashboard == cursor.IsAccountDashboard { result = result[idx+1:] break } @@ -1892,7 +1844,7 @@ func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Contex // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -1902,11 +1854,11 @@ func (d *DataAccessService) GetNotificationSettingsDashboards(ctx context.Contex result = result[:limit] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) } - p, err := utils.GetPagingFromData(result, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(result, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } diff --git a/backend/pkg/api/data_access/vdb.go b/backend/pkg/api/data_access/vdb.go index 8eadc6352..f87f5f994 100644 --- a/backend/pkg/api/data_access/vdb.go +++ b/backend/pkg/api/data_access/vdb.go @@ -35,7 +35,7 @@ type ValidatorDashboardRepository interface { AddValidatorDashboardValidatorsByGraffiti(ctx context.Context, dashboardId t.VDBIdPrimary, groupId uint64, graffiti string, limit uint64) ([]t.VDBPostValidatorsData, error) RemoveValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBIdPrimary, validators []t.VDBValidator) error - GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) + GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor t.ValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) GetValidatorDashboardValidatorsCount(ctx context.Context, dashboardId t.VDBIdPrimary) (uint64, error) CreateValidatorDashboardPublicId(ctx context.Context, dashboardId t.VDBIdPrimary, name string, shareGroups bool) (*t.VDBPublicId, error) @@ -48,7 +48,7 @@ type ValidatorDashboardRepository interface { GetLatestExportedChartTs(ctx context.Context, aggregation enums.ChartAggregation) (uint64, error) - GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) + GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search t.VDBSummarySearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) GetValidatorDashboardGroupSummary(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod, protocolModes t.VDBProtocolModes) (*t.VDBGroupSummaryData, error) GetValidatorDashboardSummaryChart(ctx context.Context, dashboardId t.VDBId, groupIds []int64, efficiencyType enums.VDBSummaryChartEfficiencyType, aggregation enums.ChartAggregation, afterTs uint64, beforeTs uint64) (*t.ChartData[int, float64], error) GetValidatorDashboardSummaryValidators(ctx context.Context, dashboardId t.VDBId, groupId int64) (*t.VDBGeneralSummaryValidators, error) @@ -56,28 +56,28 @@ type ValidatorDashboardRepository interface { GetValidatorDashboardSlashingsSummaryValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod) (*t.VDBSlashingsSummaryValidators, error) GetValidatorDashboardProposalSummaryValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, period enums.TimePeriod) (*t.VDBProposalSummaryValidators, error) - GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) + GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor t.RewardsCursor, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) GetValidatorDashboardGroupRewards(ctx context.Context, dashboardId t.VDBId, groupId int64, epoch uint64, protocolModes t.VDBProtocolModes) (*t.VDBGroupRewardsData, error) GetValidatorDashboardRewardsChart(ctx context.Context, dashboardId t.VDBId, protocolModes t.VDBProtocolModes) (*t.ChartData[int, decimal.Decimal], error) - GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor string, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) + GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor t.ValidatorDutiesCursor, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) - GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBBlocksColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) + GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor t.BlocksCursor, colSort t.Sort[enums.VDBBlocksColumn], search t.VDBBlocksSearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) GetValidatorDashboardHeatmap(ctx context.Context, dashboardId t.VDBId, protocolModes t.VDBProtocolModes, aggregation enums.ChartAggregation, afterTs uint64, beforeTs uint64) (*t.VDBHeatmap, error) GetValidatorDashboardGroupHeatmap(ctx context.Context, dashboardId t.VDBId, groupId uint64, protocolModes t.VDBProtocolModes, aggregation enums.ChartAggregation, timestamp uint64) (*t.VDBHeatmapTooltipData, error) - GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) - GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) + GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.ELDepositsCursor, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) + GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.CLDepositsCursor, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) GetValidatorDashboardTotalElDeposits(ctx context.Context, dashboardId t.VDBId) (*t.VDBTotalExecutionDepositsData, error) GetValidatorDashboardTotalClDeposits(ctx context.Context, dashboardId t.VDBId) (*t.VDBTotalConsensusDepositsData, error) - GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) + GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor t.WithdrawalsCursor, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) GetValidatorDashboardTotalWithdrawals(ctx context.Context, dashboardId t.VDBId, search string, protocolModes t.VDBProtocolModes) (*t.VDBTotalWithdrawalsData, error) - GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) + GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor t.VDBRocketPoolCursor, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) GetValidatorDashboardTotalRocketPool(ctx context.Context, dashboardId t.VDBId, search string) (*t.VDBRocketPoolTableRow, error) - GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node, cursor string, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) + GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node string, cursor t.VDBRocketPoolMinipoolsCursor, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) GetValidatorDashboardMobileWidget(ctx context.Context, dashboardId t.VDBIdPrimary) (*t.MobileWidgetData, error) } diff --git a/backend/pkg/api/data_access/vdb_blocks.go b/backend/pkg/api/data_access/vdb_blocks.go index 7fda00911..921fd29e4 100644 --- a/backend/pkg/api/data_access/vdb_blocks.go +++ b/backend/pkg/api/data_access/vdb_blocks.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "fmt" - "regexp" "slices" "strings" "time" @@ -23,33 +22,17 @@ import ( "github.com/shopspring/decimal" ) -func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBBlocksColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, dashboardId t.VDBId, cursor t.BlocksCursor, colSort t.Sort[enums.VDBBlocksColumn], search t.VDBBlocksSearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBBlocksTableRow, *t.Paging, error) { // @DATA-ACCESS incorporate protocolModes // ------------------------------------- // Setup var err error - var currentCursor t.BlocksCursor validatorMapping, err := d.services.GetCurrentValidatorMapping() if err != nil { return nil, nil, err } - // TODO @LuccaBitfly move validation to handler? - if cursor != "" { - if currentCursor, err = utils.StringToCursor[t.BlocksCursor](cursor); err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as BlocksCursor: %w", err) - } - } - - searchPubkey := regexp.MustCompile(`^0x[0-9a-fA-F]{96}$`).MatchString(search) - searchGroup := !dashboardId.AggregateGroups && regexp.MustCompile(`^[a-zA-Z0-9_\-.\ ]+$`).MatchString(search) - searchIndex := regexp.MustCompile(`^[0-9]+$`).MatchString(search) - - if search != "" && !searchPubkey && !searchGroup && !searchIndex { - return make([]t.VDBBlocksTableRow, 0), &t.Paging{}, nil - } - validators := goqu.T("validators") // could adapt data type to make handling as table/alias less confusing blocks := goqu.T("blocks") groups := goqu.T("groups") @@ -74,22 +57,22 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das Where(validators.Col("dashboard_id").Eq(dashboardId.Id)) // apply search filters searches := []exp.Expression{} - if searchIndex { - searches = append(searches, validators.Col("validator_index").Eq(search)) + if search.Index.Enabled { + searches = append(searches, validators.Col("validator_index").Eq(search.Index.Value)) } - if searchGroup { + if search.Group.Enabled { filteredValidatorsDs = filteredValidatorsDs. InnerJoin(goqu.T("users_val_dashboards_groups").As(groups), goqu.On( validators.Col("group_id").Eq(groups.Col("id")), validators.Col("dashboard_id").Eq(groups.Col("dashboard_id")), )) searches = append(searches, - goqu.L("LOWER(?)", groups.Col("name")).Like(strings.Replace(strings.ToLower(search), "_", "\\_", -1)+"%"), + goqu.L("LOWER(?)", groups.Col("name")).Like(strings.Replace(search.Group.Value, "_", "\\_", -1)+"%"), ) } - if searchPubkey { - index, ok := validatorMapping.ValidatorIndices[search] - if !ok && !searchGroup && !searchIndex { + if search.Pubkey.Enabled { + index, ok := validatorMapping.ValidatorIndices[search.Pubkey.Value] + if !ok && !search.Group.Enabled && !search.Index.Enabled { // searched pubkey doesn't exist, don't even need to query anything return make([]t.VDBBlocksTableRow, 0), &t.Paging{}, nil } @@ -103,8 +86,8 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das } else { validatorList := make([]t.VDBValidator, 0, len(dashboardId.Validators)) for _, validator := range dashboardId.Validators { - if searchIndex && fmt.Sprint(validator) != search || - searchPubkey && validator != validatorMapping.ValidatorIndices[search] { + if search.Index.Filtered(validator) || + search.Pubkey.Enabled && validator != validatorMapping.ValidatorIndices[search.Pubkey.Value] { continue } filteredValidators = append(filteredValidators, validatorGroup{ @@ -112,7 +95,7 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das Group: t.DefaultGroupId, }) validatorList = append(validatorList, validator) - if searchIndex || searchPubkey { + if search.Index.Enabled || search.Pubkey.Enabled { break } } @@ -181,24 +164,24 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das // 3. Sorting and pagination defaultColumns := []t.SortColumn{ - {Column: enums.VDBBlocksColumns.Slot.ToExpr(), Desc: true, Offset: currentCursor.Slot}, + {Column: enums.VDBBlocksColumns.Slot.ToExpr(), Desc: true, Offset: cursor.Slot}, } var offset any switch colSort.Column { case enums.VDBBlocksColumns.Proposer: - offset = currentCursor.Proposer + offset = cursor.Proposer case enums.VDBBlocksColumns.Block: - offset = currentCursor.Block - if !currentCursor.Block.Valid { + offset = cursor.Block + if !cursor.Block.Valid { offset = nil } case enums.VDBBlocksColumns.Status: - offset = fmt.Sprintf("%d", currentCursor.Status) // type of 'status' column is text for some reason + offset = fmt.Sprintf("%d", cursor.Status) // type of 'status' column is text for some reason case enums.VDBBlocksColumns.ProposerReward: - offset = currentCursor.Reward + offset = cursor.Reward } - order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc, Offset: offset}, currentCursor.GenericCursor) + order, directions, err := applySortAndPagination(defaultColumns, t.SortColumn{Column: colSort.Column.ToExpr(), Desc: colSort.Desc, Offset: offset}, cursor.GenericCursor) if err != nil { return nil, nil, err } @@ -215,9 +198,9 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das latestSlot := cache.LatestSlot.Get() onlyPrimarySort := colSort.Column == enums.VDBBlockSlot if !(onlyPrimarySort || colSort.Column == enums.VDBBlockBlock) || - !currentCursor.IsValid() || - currentCursor.Slot > latestSlot+1 || - colSort.Desc == currentCursor.Reverse { + !cursor.IsValid() || + cursor.Slot > latestSlot+1 || + colSort.Desc == cursor.Reverse { dutiesInfo, err := d.services.GetCurrentDutiesInfo() if err == nil { if dashboardId.Validators == nil { @@ -346,7 +329,7 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das if moreDataFlag { proposals = proposals[:len(proposals)-1] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(proposals) } @@ -475,11 +458,11 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das contractIdx += 1 } } - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return data, &t.Paging{}, nil } - p, err := utils.GetPagingFromData(proposals, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(proposals, cursor, moreDataFlag) if err != nil { return nil, nil, err } diff --git a/backend/pkg/api/data_access/vdb_deposits.go b/backend/pkg/api/data_access/vdb_deposits.go index 0f81e1d93..dcd3b772c 100644 --- a/backend/pkg/api/data_access/vdb_deposits.go +++ b/backend/pkg/api/data_access/vdb_deposits.go @@ -20,17 +20,8 @@ import ( "github.com/shopspring/decimal" ) -func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) { - var err error +func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.ELDepositsCursor, limit uint64) ([]t.VDBExecutionDepositsTableRow, *t.Paging, error) { currentDirection := enums.DESC // TODO: expose over parameter - var currentCursor t.ELDepositsCursor - - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.ELDepositsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as ELDepositsCursor: %w", err) - } - } // Resolve validator indices to pubkeys byteaArray, err := d.getValidatorPubkeys(dashboardId) @@ -88,12 +79,12 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, params := []interface{}{filter} filterFragment := ` ORDER BY ed.block_number DESC, ed.log_index DESC` - if currentCursor.IsValid() { + if cursor.IsValid() { filterFragment = ` AND (ed.block_number < $2 or (ed.block_number = $2 and ed.log_index < $3)) ` + filterFragment - params = append(params, currentCursor.BlockNumber, currentCursor.LogIndex) + params = append(params, cursor.BlockNumber, cursor.LogIndex) } - if currentDirection == enums.ASC && !currentCursor.IsReverse() || currentDirection == enums.DESC && currentCursor.IsReverse() { + if currentDirection == enums.ASC && !cursor.IsReverse() || currentDirection == enums.DESC && cursor.IsReverse() { filterFragment = strings.Replace(strings.Replace(filterFragment, "<", ">", -1), "DESC", "ASC", -1) } @@ -194,7 +185,7 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, var paging t.Paging moreDataFlag := len(responseData) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return responseData, &paging, nil } @@ -204,13 +195,13 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, data = data[:len(data)-1] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { // Invert query result so response matches requested direction slices.Reverse(responseData) slices.Reverse(data) } - p, err := utils.GetPagingFromData(data, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(data, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } @@ -218,17 +209,8 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, return responseData, p, nil } -func (d *DataAccessService) GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor string, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) { - var err error +func (d *DataAccessService) GetValidatorDashboardClDeposits(ctx context.Context, dashboardId t.VDBId, cursor t.CLDepositsCursor, limit uint64) ([]t.VDBConsensusDepositsTableRow, *t.Paging, error) { currentDirection := enums.DESC // TODO: expose over parameter - var currentCursor t.CLDepositsCursor - - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.CLDepositsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as CLDepositsCursor: %w", err) - } - } // Resolve validator indices to pubkeys byteaArray, err := d.getValidatorPubkeys(dashboardId) @@ -279,12 +261,12 @@ func (d *DataAccessService) GetValidatorDashboardClDeposits(ctx context.Context, params := []interface{}{filter} filterFragment := ` ORDER BY bd.block_slot DESC, bd.block_index DESC` - if currentCursor.IsValid() { + if cursor.IsValid() { filterFragment = ` AND (bd.block_slot < $2 or (bd.block_slot = $2 and bd.block_index < $3)) ` + filterFragment - params = append(params, currentCursor.Slot, currentCursor.SlotIndex) + params = append(params, cursor.Slot, cursor.SlotIndex) } - if currentDirection == enums.ASC && !currentCursor.IsReverse() || currentDirection == enums.DESC && currentCursor.IsReverse() { + if currentDirection == enums.ASC && !cursor.IsReverse() || currentDirection == enums.DESC && cursor.IsReverse() { filterFragment = strings.Replace(strings.Replace(filterFragment, "<", ">", -1), "DESC", "ASC", -1) } @@ -334,7 +316,7 @@ func (d *DataAccessService) GetValidatorDashboardClDeposits(ctx context.Context, var paging t.Paging moreDataFlag := len(responseData) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return responseData, &paging, nil } @@ -344,13 +326,13 @@ func (d *DataAccessService) GetValidatorDashboardClDeposits(ctx context.Context, data = data[:len(data)-1] } - if currentCursor.IsReverse() { + if cursor.IsReverse() { // Invert query result so response matches requested direction slices.Reverse(responseData) slices.Reverse(data) } - p, err := utils.GetPagingFromData(data, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(data, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } diff --git a/backend/pkg/api/data_access/vdb_management.go b/backend/pkg/api/data_access/vdb_management.go index db4037818..1d1f93d6c 100644 --- a/backend/pkg/api/data_access/vdb_management.go +++ b/backend/pkg/api/data_access/vdb_management.go @@ -7,7 +7,6 @@ import ( "fmt" "math/big" "sort" - "strconv" "strings" "sync" @@ -626,17 +625,7 @@ func (d *DataAccessService) GetValidatorDashboardGroupCount(ctx context.Context, return count, err } -func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor string, colSort t.Sort[enums.VDBManageValidatorsColumn], search string, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) { - // Initialize the cursor - var currentCursor t.ValidatorsCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.ValidatorsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as ValidatorsCursor: %w", err) - } - } - +func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, dashboardId t.VDBId, groupId int64, cursor t.ValidatorsCursor, colSort t.Sort[enums.VDBManageValidatorsColumn], search t.VDBManageValidatorsSearch, limit uint64) ([]t.VDBManageValidatorsTableRow, *t.Paging, error) { type ValidatorGroupInfo struct { GroupId uint64 GroupName string @@ -720,21 +709,14 @@ func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, row.QueuePosition = &activationIndex } - if search == "" { - data = append(data, row) - } else { - index, err := strconv.ParseUint(search, 10, 64) - indexSearch := err == nil && index == row.Index - - pubKey := strings.ToLower(strings.TrimPrefix(search, "0x")) - pubkeySearch := pubKey == strings.TrimPrefix(string(row.PublicKey), "0x") - - groupNameSearch := search == validatorGroupMap[validator].GroupName - - if indexSearch || pubkeySearch || groupNameSearch { - data = append(data, row) - } + // TODO should apply filter to db query + if search.IsEnabled() && + search.Index.Filtered(row.Index) && + search.Pubkey.Filtered(string(row.PublicKey)) && + search.Group.Filtered(validatorGroupMap[validator].GroupName) { + continue } + data = append(data, row) } // no data found (searched for something that does not exist) @@ -771,9 +753,9 @@ func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, // Find the index for the cursor and limit the data var cursorIndex uint64 - if currentCursor.IsValid() { + if cursor.IsValid() { for idx, row := range data { - if row.Index == currentCursor.Index { + if row.Index == cursor.Index { cursorIndex = uint64(idx) break } @@ -781,7 +763,7 @@ func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, } var result []t.VDBManageValidatorsTableRow - if currentCursor.IsReverse() { + if cursor.IsReverse() { // opposite direction var limitCutoff uint64 if cursorIndex > limit+1 { @@ -789,7 +771,7 @@ func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, } result = data[limitCutoff:cursorIndex] } else { - if currentCursor.IsValid() { + if cursor.IsValid() { cursorIndex++ } limitCutoff := utilMath.MinU64(cursorIndex+limit+1, uint64(len(data))) @@ -798,21 +780,21 @@ func (d *DataAccessService) GetValidatorDashboardValidators(ctx context.Context, // flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // no paging required return result, &paging, nil } // remove the last entry from data as it is only required for the check if moreDataFlag { - if currentCursor.IsReverse() { + if cursor.IsReverse() { result = result[1:] } else { result = result[:len(result)-1] } } - p, err := utils.GetPagingFromData(result, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(result, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } diff --git a/backend/pkg/api/data_access/vdb_rewards.go b/backend/pkg/api/data_access/vdb_rewards.go index 1221f9596..2967ff23e 100644 --- a/backend/pkg/api/data_access/vdb_rewards.go +++ b/backend/pkg/api/data_access/vdb_rewards.go @@ -22,25 +22,16 @@ import ( "golang.org/x/sync/errgroup" ) -func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, dashboardId t.VDBId, cursor t.RewardsCursor, colSort t.Sort[enums.VDBRewardsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBRewardsTableRow, *t.Paging, error) { // @DATA-ACCESS incorporate protocolModes result := make([]t.VDBRewardsTableRow, 0) var paging t.Paging wg := errgroup.Group{} - - // Initialize the cursor - var currentCursor t.RewardsCursor var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.RewardsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as RewardsCursor: %w", err) - } - } // Prepare the sorting - isReverseDirection := (colSort.Desc && !currentCursor.IsReverse()) || (!colSort.Desc && currentCursor.IsReverse()) + isReverseDirection := (colSort.Desc && !cursor.IsReverse()) || (!colSort.Desc && cursor.IsReverse()) sortSearchDirection := ">" if isReverseDirection { sortSearchDirection = "<" @@ -122,28 +113,28 @@ func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, da Where(goqu.L("e.validator_index IN (SELECT validator_index FROM validators)")) elDs = elDs. Where(goqu.L("v.dashboard_id = ?", dashboardId.Id)) - if currentCursor.IsValid() { - if currentCursor.IsReverse() { - if currentCursor.GroupId == t.AllGroups { + if cursor.IsValid() { + if cursor.IsReverse() { + if cursor.GroupId == t.AllGroups { // The cursor is on the total rewards so get the data for all groups excluding the cursor epoch - rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(currentCursor.Epoch).Unix())) - elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s ?", sortSearchDirection), currentCursor.Epoch)) + rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(cursor.Epoch).Unix())) + elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s ?", sortSearchDirection), cursor.Epoch)) } else { // The cursor is on a specific group, get the data for the whole epoch since we could need it for the total rewards - rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s= fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(currentCursor.Epoch).Unix())) - elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s= ?", sortSearchDirection), currentCursor.Epoch)) + rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s= fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(cursor.Epoch).Unix())) + elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s= ?", sortSearchDirection), cursor.Epoch)) } } else { - if currentCursor.GroupId == t.AllGroups { + if cursor.GroupId == t.AllGroups { // The cursor is on the total rewards so get the data for all groups including the cursor epoch - rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s= fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(currentCursor.Epoch).Unix())) - elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s= ?", sortSearchDirection), currentCursor.Epoch)) + rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s= fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(cursor.Epoch).Unix())) + elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s= ?", sortSearchDirection), cursor.Epoch)) } else { // The cursor is on a specific group so get the data for groups before/after it rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("(e.epoch_timestamp %[1]s fromUnixTimestamp(?) OR (e.epoch_timestamp = fromUnixTimestamp(?) AND v.group_id %[1]s ?))", sortSearchDirection), - utils.EpochToTime(currentCursor.Epoch).Unix(), utils.EpochToTime(currentCursor.Epoch).Unix(), currentCursor.GroupId)) + utils.EpochToTime(cursor.Epoch).Unix(), utils.EpochToTime(cursor.Epoch).Unix(), cursor.GroupId)) elDs = elDs.Where(goqu.L(fmt.Sprintf("(b.epoch %[1]s ? OR (b.epoch = ? AND v.group_id %[1]s ?))", sortSearchDirection), - currentCursor.Epoch, currentCursor.Epoch, currentCursor.GroupId)) + cursor.Epoch, cursor.Epoch, cursor.GroupId)) } } } @@ -262,9 +253,9 @@ func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, da Where(goqu.L("b.proposer = ANY(?)", pq.Array(dashboardId.Validators))). GroupBy(goqu.L("b.epoch")) - if currentCursor.IsValid() { - rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(currentCursor.Epoch).Unix())) - elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s ?", sortSearchDirection), currentCursor.Epoch)) + if cursor.IsValid() { + rewardsDs = rewardsDs.Where(goqu.L(fmt.Sprintf("e.epoch_timestamp %s fromUnixTimestamp(?)", sortSearchDirection), utils.EpochToTime(cursor.Epoch).Unix())) + elDs = elDs.Where(goqu.L(fmt.Sprintf("b.epoch %s ?", sortSearchDirection), cursor.Epoch)) } if search != "" { if epochSearch == -1 && indexSearch == -1 { @@ -464,15 +455,15 @@ func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, da } // Reverse the data if the cursor is reversed to correct it to the requested direction - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(resultWoTotal) } // Place the total rewards in the result data at the correct position and ignore group data that is not searched for // Ascending or descending order makes no difference but the cursor direction does previousEpoch := int64(-1) - if currentCursor.IsValid() && !currentCursor.IsReverse() { - previousEpoch = int64(currentCursor.Epoch) + if cursor.IsValid() && !cursor.IsReverse() { + previousEpoch = int64(cursor.Epoch) } for _, res := range resultWoTotal { if previousEpoch != int64(res.Epoch) { @@ -482,7 +473,7 @@ func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, da } // If we reach a specific group cursor which should only happen if the cursor is reversed don't include it and stop - if currentCursor.IsReverse() && currentCursor.Epoch == res.Epoch && currentCursor.GroupId == res.GroupId { + if cursor.IsReverse() && cursor.Epoch == res.Epoch && cursor.GroupId == res.GroupId { break } // If we don't search for specific groups or the group is in the search add the row @@ -494,21 +485,21 @@ func (d *DataAccessService) GetValidatorDashboardRewards(ctx context.Context, da // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } // Remove the last entries from data if moreDataFlag { - if currentCursor.IsReverse() { + if cursor.IsReverse() { result = result[len(result)-int(limit):] } else { result = result[:limit] } } - p, err := utils.GetPagingFromData(result, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(result, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } @@ -903,7 +894,7 @@ func (d *DataAccessService) GetValidatorDashboardRewardsChart(ctx context.Contex return &result, nil } -func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor string, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, dashboardId t.VDBId, epoch uint64, groupId int64, cursor t.ValidatorDutiesCursor, colSort t.Sort[enums.VDBDutiesColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBEpochDutiesTableRow, *t.Paging, error) { result := make([]t.VDBEpochDutiesTableRow, 0) var paging t.Paging @@ -914,18 +905,8 @@ func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, das groupId = t.AllGroups } - // Initialize the cursor - var currentCursor t.ValidatorDutiesCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.ValidatorDutiesCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as ValidatorDutiesCursor: %w", err) - } - } - // Prepare the sorting - isReverseDirection := (colSort.Desc && !currentCursor.IsReverse()) || (!colSort.Desc && currentCursor.IsReverse()) + isReverseDirection := (colSort.Desc && !cursor.IsReverse()) || (!colSort.Desc && cursor.IsReverse()) // Analyze the search term indexSearch := int64(-1) @@ -1100,7 +1081,7 @@ func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, das return nil }) - err = wg.Wait() + err := wg.Wait() if err != nil { return nil, nil, fmt.Errorf("error retrieving validator dashboard rewards data: %w", err) } @@ -1234,11 +1215,11 @@ func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, das }) // Remove data before the cursor - if currentCursor.IsValid() { + if cursor.IsValid() { cursorIndex := -1 for idx, cursorEntry := range cursorData { - if cursorEntry.Index == currentCursor.Index && cursorEntry.Reward.Equal(currentCursor.Reward) { + if cursorEntry.Index == cursor.Index && cursorEntry.Reward.Equal(cursor.Reward) { cursorIndex = idx break } @@ -1253,7 +1234,7 @@ func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, das // Flag if above limit moreDataFlag := len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -1265,12 +1246,12 @@ func (d *DataAccessService) GetValidatorDashboardDuties(ctx context.Context, das } // Reverse the data if the cursor is reversed to correct it to the requested direction - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) slices.Reverse(cursorData) } - p, err := utils.GetPagingFromData(cursorData, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(cursorData, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } diff --git a/backend/pkg/api/data_access/vdb_rocket_pool.go b/backend/pkg/api/data_access/vdb_rocket_pool.go index 90a94d3af..35eba1e80 100644 --- a/backend/pkg/api/data_access/vdb_rocket_pool.go +++ b/backend/pkg/api/data_access/vdb_rocket_pool.go @@ -7,7 +7,7 @@ import ( t "github.com/gobitfly/beaconchain/pkg/api/types" ) -func (d *DataAccessService) GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardRocketPool(ctx context.Context, dashboardId t.VDBId, cursor t.VDBRocketPoolCursor, colSort t.Sort[enums.VDBRocketPoolColumn], search string, limit uint64) ([]t.VDBRocketPoolTableRow, *t.Paging, error) { // TODO @DATA-ACCESS return d.dummy.GetValidatorDashboardRocketPool(ctx, dashboardId, cursor, colSort, search, limit) } @@ -17,7 +17,7 @@ func (d *DataAccessService) GetValidatorDashboardTotalRocketPool(ctx context.Con return d.dummy.GetValidatorDashboardTotalRocketPool(ctx, dashboardId, search) } -func (d *DataAccessService) GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node, cursor string, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardRocketPoolMinipools(ctx context.Context, dashboardId t.VDBId, node string, cursor t.VDBRocketPoolMinipoolsCursor, colSort t.Sort[enums.VDBRocketPoolMinipoolsColumn], search string, limit uint64) ([]t.VDBRocketPoolMinipoolsTableRow, *t.Paging, error) { // TODO @DATA-ACCESS return d.dummy.GetValidatorDashboardRocketPoolMinipools(ctx, dashboardId, node, cursor, colSort, search, limit) } diff --git a/backend/pkg/api/data_access/vdb_summary.go b/backend/pkg/api/data_access/vdb_summary.go index c0fa0372c..a54828b6a 100644 --- a/backend/pkg/api/data_access/vdb_summary.go +++ b/backend/pkg/api/data_access/vdb_summary.go @@ -8,7 +8,6 @@ import ( "math/big" "slices" "sort" - "strconv" "strings" "sync" "time" @@ -28,7 +27,8 @@ import ( "golang.org/x/sync/errgroup" ) -func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, dashboardId t.VDBId, period enums.TimePeriod, cursor string, colSort t.Sort[enums.VDBSummaryColumn], search t.VDBSummarySearch, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBSummaryTableRow, *t.Paging, error) { + // TODO either implement or remove cursor parameter // @DATA-ACCESS incorporate protocolModes result := make([]t.VDBSummaryTableRow, 0) var paging t.Paging @@ -41,30 +41,26 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da return nil, nil, err } - // Searching for a group name is not supported when aggregating groups or for guest dashboards - groupNameSearchEnabled := !dashboardId.AggregateGroups && dashboardId.Validators == nil - // Analyze the search term - searchValidator := -1 - if search != "" { - if strings.HasPrefix(search, "0x") && utils.IsHash(search) { - search = strings.ToLower(search) - + searchValidator := struct { + Enabled bool + Index uint64 + }{Enabled: search.Index.Enabled || search.Pubkey.Enabled} + if search.IsEnabled() { + if search.Pubkey.Enabled { // Get the current validator state to convert pubkey to index validatorMapping, err := d.services.GetCurrentValidatorMapping() if err != nil { return nil, nil, err } - if index, ok := validatorMapping.ValidatorIndices[search]; ok { - searchValidator = int(index) + if index, ok := validatorMapping.ValidatorIndices[search.Pubkey.Value]; ok { + searchValidator.Index = index } else { // No validator index for pubkey found, return empty results return result, &paging, nil } - } else if number, err := strconv.ParseUint(search, 10, 64); err == nil { - searchValidator = int(number) - } else if !groupNameSearchEnabled { - return result, &paging, nil + } else if search.Index.Enabled { + searchValidator.Index = search.Index.Value } } @@ -74,12 +70,12 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da if dashboardId.Validators != nil { validatorFound := false for _, validator := range dashboardId.Validators { - if searchValidator != -1 && int(validator) == searchValidator { + if searchValidator.Enabled && validator == searchValidator.Index { validatorFound = true } validators = append(validators, validator) } - if searchValidator != -1 && !validatorFound { + if searchValidator.Enabled && !validatorFound { // The searched validator is not part of the dashboard return result, &paging, nil } @@ -148,7 +144,7 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da InnerJoin(goqu.L("validators v"), goqu.On(goqu.L("r.validator_index = v.validator_index"))). Where(goqu.L("r.validator_index IN (SELECT validator_index FROM validators)")) - if groupNameSearchEnabled && (search != "" || colSort.Column == enums.VDBSummaryColumns.Group) { + if search.Group.Enabled && colSort.Column == enums.VDBSummaryColumns.Group { // Get the group names since we can filter and/or sort for them ds = ds. SelectAppend(goqu.L("g.name AS group_name")). @@ -259,7 +255,8 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da // ------------------------------------------------------------------------------------------------------------------ // Sort by group name, after this the name is no longer relevant - if groupNameSearchEnabled && colSort.Column == enums.VDBSummaryColumns.Group { + // TODO should apply filter to db query + if search.Group.Enabled && colSort.Column == enums.VDBSummaryColumns.Group { sort.Slice(queryResult, func(i, j int) bool { if colSort.Desc { return queryResult[i].GroupName > queryResult[j].GroupName @@ -380,11 +377,11 @@ func (d *DataAccessService) GetValidatorDashboardSummary(ctx context.Context, da total.SyncScheduled += queryEntry.SyncScheduled // If the search permits it add the entry to the result - if search != "" { - prefixSearch := strings.ToLower(search) + // TODO should apply filter to db query + if search.IsEnabled() { for _, validatorIndex := range queryEntry.ValidatorIndices { - if searchValidator != -1 && validatorIndex == uint64(searchValidator) || - (groupNameSearchEnabled && strings.HasPrefix(strings.ToLower(queryEntry.GroupName), prefixSearch)) { + if searchValidator.Enabled && validatorIndex == searchValidator.Index || + search.Group.Enabled && strings.HasPrefix(strings.ToLower(queryEntry.GroupName), search.Group.Value) { result = append(result, resultEntry) break } diff --git a/backend/pkg/api/data_access/vdb_withdrawals.go b/backend/pkg/api/data_access/vdb_withdrawals.go index 93124cd73..a7c47b479 100644 --- a/backend/pkg/api/data_access/vdb_withdrawals.go +++ b/backend/pkg/api/data_access/vdb_withdrawals.go @@ -24,24 +24,14 @@ import ( "github.com/shopspring/decimal" ) -func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor string, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) { +func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context, dashboardId t.VDBId, cursor t.WithdrawalsCursor, colSort t.Sort[enums.VDBWithdrawalsColumn], search string, limit uint64, protocolModes t.VDBProtocolModes) ([]t.VDBWithdrawalsTableRow, *t.Paging, error) { result := make([]t.VDBWithdrawalsTableRow, 0) var paging t.Paging - // Initialize the cursor - var currentCursor t.WithdrawalsCursor - var err error - if cursor != "" { - currentCursor, err = utils.StringToCursor[t.WithdrawalsCursor](cursor) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse passed cursor as WithdrawalsCursor: %w", err) - } - } - // Prepare the sorting sortSearchDirection := ">" sortSearchOrder := " ASC" - if (colSort.Desc && !currentCursor.IsReverse()) || (!colSort.Desc && currentCursor.IsReverse()) { + if (colSort.Desc && !cursor.IsReverse()) || (!colSort.Desc && cursor.IsReverse()) { sortSearchDirection = "<" sortSearchOrder = " DESC" } @@ -147,28 +137,28 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context case enums.VDBWithdrawalsColumns.Epoch, enums.VDBWithdrawalsColumns.Slot: case enums.VDBWithdrawalsColumns.Index: sortColName = "w.validatorindex" - sortColCursor = currentCursor.Index + sortColCursor = cursor.Index case enums.VDBWithdrawalsColumns.Recipient: sortColName = "w.address" - sortColCursor = currentCursor.Recipient + sortColCursor = cursor.Recipient case enums.VDBWithdrawalsColumns.Amount: sortColName = "w.amount" - sortColCursor = currentCursor.Amount + sortColCursor = cursor.Amount } if colSort.Column == enums.VDBWithdrawalsColumns.Epoch || colSort.Column == enums.VDBWithdrawalsColumns.Slot { - if currentCursor.IsValid() { + if cursor.IsValid() { // If we have a valid cursor only check the results before/after it - queryParams = append(queryParams, currentCursor.Slot, currentCursor.WithdrawalIndex) + queryParams = append(queryParams, cursor.Slot, cursor.WithdrawalIndex) whereQuery += fmt.Sprintf(" AND (w.block_slot%[1]s$%[2]d OR (w.block_slot=$%[2]d AND w.withdrawalindex%[1]s$%[3]d))", sortSearchDirection, len(queryParams)-1, len(queryParams)) } orderQuery = fmt.Sprintf(" ORDER BY w.block_slot %[1]s, w.withdrawalindex %[1]s", sortSearchOrder) } else { - if currentCursor.IsValid() { + if cursor.IsValid() { // If we have a valid cursor only check the results before/after it - queryParams = append(queryParams, sortColCursor, currentCursor.Slot, currentCursor.WithdrawalIndex) + queryParams = append(queryParams, sortColCursor, cursor.Slot, cursor.WithdrawalIndex) // The additional WHERE requirement is // WHERE sortColName>cursor OR (sortColName=cursor AND (block_slot>cursor OR (block_slot=cursor AND withdrawalindex>cursor))) @@ -255,14 +245,14 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context } // Reverse the data if the cursor is reversed to correct it to the requested direction - if currentCursor.IsReverse() { + if cursor.IsReverse() { slices.Reverse(result) slices.Reverse(cursorData) } // Find the next withdrawal if we are currently at the first page // If we have a prev_cursor but not enough data it means the next data is missing - if !currentCursor.IsValid() || (currentCursor.IsReverse() && len(result) < int(limit)) { + if !cursor.IsValid() || (cursor.IsReverse() && len(result) < int(limit)) { nextData, err := d.getNextWithdrawalRow(validators) if err != nil { return nil, nil, err @@ -282,7 +272,7 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context // Flag if above limit moreDataFlag = moreDataFlag || len(result) > int(limit) - if !moreDataFlag && !currentCursor.IsValid() { + if !moreDataFlag && !cursor.IsValid() { // No paging required return result, &paging, nil } @@ -294,7 +284,7 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context } } - p, err := utils.GetPagingFromData(cursorData, currentCursor, moreDataFlag) + p, err := utils.GetPagingFromData(cursorData, cursor, moreDataFlag) if err != nil { return nil, nil, fmt.Errorf("failed to get paging: %w", err) } diff --git a/backend/pkg/api/enums/enums.go b/backend/pkg/api/enums/enums.go index 95ccd0edb..1a735c3cc 100644 --- a/backend/pkg/api/enums/enums.go +++ b/backend/pkg/api/enums/enums.go @@ -1,6 +1,8 @@ package enums -import "time" +import ( + "time" +) type Enum interface { Int() int diff --git a/backend/pkg/api/handlers/handler_service.go b/backend/pkg/api/handlers/handler_service.go index 32a920bf9..2640d7f85 100644 --- a/backend/pkg/api/handlers/handler_service.go +++ b/backend/pkg/api/handlers/handler_service.go @@ -269,6 +269,10 @@ func writeResponse(w http.ResponseWriter, r *http.Request, statusCode int, respo } } +func emptyPagingResponse() interface{} { + return types.ApiPagingResponse[any]{Paging: types.Paging{}, Data: []any{}} +} + func returnError(w http.ResponseWriter, r *http.Request, code int, err error) { response := types.ApiErrorResponse{ Error: err.Error(), diff --git a/backend/pkg/api/handlers/input_validation.go b/backend/pkg/api/handlers/input_validation.go index df3d53f1d..0b79f1f33 100644 --- a/backend/pkg/api/handlers/input_validation.go +++ b/backend/pkg/api/handlers/input_validation.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/gobitfly/beaconchain/pkg/api/enums" "github.com/gobitfly/beaconchain/pkg/api/types" + "github.com/gobitfly/beaconchain/pkg/commons/utils" "github.com/gorilla/mux" "github.com/invopop/jsonschema" "github.com/shopspring/decimal" @@ -42,6 +43,23 @@ var ( reJsonContentType = regexp.MustCompile(`^application\/json(;.*)?$`) ) +var searchEnumsRegexMapping = map[types.SearchType]*regexp.Regexp{ + types.SearchTypeName: reName, + types.SearchTypeInteger: reInteger, + types.SearchTypeEthereumAddress: reEthereumAddress, + types.SearchTypeWithdrawalCredential: reWithdrawalCredential, + types.SearchTypeEnsName: reEnsName, + types.SearchTypeGraffiti: reGraffiti, + types.SearchTypeEmail: reEmail, + types.SearchTypePassword: rePassword, + types.SearchTypeEmailUserToken: reEmailUserToken, + types.SearchTypeJsonContentType: reJsonContentType, + // Validator Dashboard + types.SearchTypeValidatorDashboardPublicId: reValidatorDashboardPublicId, + types.SearchTypeValidatorPublicKeyWithPrefix: reValidatorPublicKeyWithPrefix, + types.SearchTypeValidatorPublicKey: reValidatorPublicKey, +} + const ( maxNameLength = 50 maxValidatorsInList = 20 @@ -348,15 +366,15 @@ func (v *validationError) checkUintMinMax(param string, min uint64, max uint64, return checkMinMax(v, v.checkUint(param, paramName), min, max, paramName) } -type Paging struct { - cursor string +type Paging[T types.CursorLike] struct { + cursor T limit uint64 search string } -func (v *validationError) checkPagingParams(q url.Values) Paging { - paging := Paging{ - cursor: q.Get("cursor"), +func checkPagingParams[T types.CursorLike](v *validationError, q url.Values) Paging[T] { + paging := Paging[T]{ + cursor: *new(T), limit: defaultReturnLimit, search: q.Get("search"), } @@ -365,8 +383,12 @@ func (v *validationError) checkPagingParams(q url.Values) Paging { paging.limit = v.checkUintMinMax(limitStr, 1, maxQueryLimit, "limit") } - if paging.cursor != "" { - paging.cursor = v.checkRegex(reCursor, paging.cursor, "cursor") + if q.Get("cursor") != "" { + cursor, err := utils.StringToCursor[T](v.checkRegex(reCursor, q.Get("cursor"), "cursor")) + if err != nil { + v.add("cursor", fmt.Sprintf("give value '%s' is not valid: %v", q.Get("cursor"), err)) + } + paging.cursor = cursor } return paging @@ -417,6 +439,22 @@ func checkSort[T enums.EnumFactory[T]](v *validationError, sortString string) *t return &types.Sort[T]{Column: sortCol, Desc: desc} } +// returns false if the search string doesn't match any search type +func checkSearch[T types.Searchable](search T, searchString string) (bool, error) { + if searchString == "" { + return true, nil + } + search.SetSearchValue(searchString) + for _, acceptedSearchType := range search.GetSearches() { + if searchEnumsRegexMapping[acceptedSearchType].MatchString(searchString) { + if err := search.SetSearchType(acceptedSearchType); err != nil { + return false, err + } + } + } + return search.HasAnyMatches(), nil +} + func (v *validationError) checkProtocolModes(protocolModes string) types.VDBProtocolModes { var modes types.VDBProtocolModes if protocolModes == "" { diff --git a/backend/pkg/api/handlers/internal.go b/backend/pkg/api/handlers/internal.go index d04b3f4f6..91e52891a 100644 --- a/backend/pkg/api/handlers/internal.go +++ b/backend/pkg/api/handlers/internal.go @@ -380,15 +380,23 @@ func (h *HandlerService) InternalGetValidatorDashboardMobileValidators(w http.Re return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.VDBMobileValidatorsCursor](&v, q) groupId := v.checkGroupId(q.Get("group_id"), allowEmpty) period := checkEnum[enums.TimePeriod](&v, q.Get("period"), "period") sort := checkSort[enums.VDBManageValidatorsColumn](&v, q.Get("sort")) + search := &types.VDBManageValidatorsSearch{DashboardId: *dashboardId} + if searchEnabled, err := checkSearch(search, pagingParams.search); err != nil { + handleErr(w, r, err) + return + } else if !searchEnabled { + returnOk(w, r, emptyPagingResponse()) + return + } if v.hasErrors() { handleErr(w, r, v) return } - data, paging, err := h.daService.GetValidatorDashboardMobileValidators(r.Context(), *dashboardId, groupId, period, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + data, paging, err := h.daService.GetValidatorDashboardMobileValidators(r.Context(), *dashboardId, groupId, period, pagingParams.cursor, *sort, *search, pagingParams.limit) if err != nil { handleErr(w, r, err) return diff --git a/backend/pkg/api/handlers/public.go b/backend/pkg/api/handlers/public.go index a628bf26c..98965ee27 100644 --- a/backend/pkg/api/handlers/public.go +++ b/backend/pkg/api/handlers/public.go @@ -694,13 +694,22 @@ func (h *HandlerService) PublicGetValidatorDashboardValidators(w http.ResponseWr } q := r.URL.Query() groupId := v.checkGroupId(q.Get("group_id"), allowEmpty) - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.ValidatorsCursor](&v, q) sort := checkSort[enums.VDBManageValidatorsColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) return } - data, paging, err := h.getDataAccessor(r).GetValidatorDashboardValidators(r.Context(), *dashboardId, groupId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit) + search := &types.VDBManageValidatorsSearch{DashboardId: *dashboardId} + if searchEnabled, err := checkSearch(search, pagingParams.search); err != nil { + handleErr(w, r, err) + return + } else if !searchEnabled { + returnOk(w, r, emptyPagingResponse()) + return + } + + data, paging, err := h.getDataAccessor(r).GetValidatorDashboardValidators(r.Context(), *dashboardId, groupId, pagingParams.cursor, *sort, *search, pagingParams.limit) if err != nil { handleErr(w, r, err) return @@ -1057,7 +1066,8 @@ func (h *HandlerService) PublicGetValidatorDashboardSummary(w http.ResponseWrite return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + // no paging present here, TODO check if it can be removed + pagingParams := checkPagingParams[types.CursorLike](&v, q) sort := checkSort[enums.VDBSummaryColumn](&v, q.Get("sort")) protocolModes := v.checkProtocolModes(q.Get("modes")) @@ -1066,8 +1076,16 @@ func (h *HandlerService) PublicGetValidatorDashboardSummary(w http.ResponseWrite handleErr(w, r, v) return } + search := &types.VDBSummarySearch{DashboardId: *dashboardId} + if searchEnabled, err := checkSearch(search, pagingParams.search); err != nil { + handleErr(w, r, err) + return + } else if !searchEnabled { + returnOk(w, r, emptyPagingResponse()) + return + } - data, paging, err := h.getDataAccessor(r).GetValidatorDashboardSummary(r.Context(), *dashboardId, period, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit, protocolModes) + data, paging, err := h.getDataAccessor(r).GetValidatorDashboardSummary(r.Context(), *dashboardId, period, "", *sort, *search, pagingParams.limit, protocolModes) if err != nil { handleErr(w, r, err) return @@ -1257,7 +1275,7 @@ func (h *HandlerService) PublicGetValidatorDashboardRewards(w http.ResponseWrite return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.RewardsCursor](&v, q) sort := checkSort[enums.VDBRewardsColumn](&v, q.Get("sort")) protocolModes := v.checkProtocolModes(q.Get("modes")) if v.hasErrors() { @@ -1380,7 +1398,7 @@ func (h *HandlerService) PublicGetValidatorDashboardDuties(w http.ResponseWriter q := r.URL.Query() groupId := v.checkGroupId(q.Get("group_id"), allowEmpty) epoch := v.checkUint(vars["epoch"], "epoch") - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.ValidatorDutiesCursor](&v, q) sort := checkSort[enums.VDBDutiesColumn](&v, q.Get("sort")) protocolModes := v.checkProtocolModes(q.Get("modes")) if v.hasErrors() { @@ -1422,15 +1440,23 @@ func (h *HandlerService) PublicGetValidatorDashboardBlocks(w http.ResponseWriter return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.BlocksCursor](&v, q) sort := checkSort[enums.VDBBlocksColumn](&v, q.Get("sort")) protocolModes := v.checkProtocolModes(q.Get("modes")) if v.hasErrors() { handleErr(w, r, v) return } + search := &types.VDBBlocksSearch{DashboardId: *dashboardId} + if searchEnabled, err := checkSearch(search, pagingParams.search); err != nil { + handleErr(w, r, err) + return + } else if !searchEnabled { + returnOk(w, r, emptyPagingResponse()) + return + } - data, paging, err := h.getDataAccessor(r).GetValidatorDashboardBlocks(r.Context(), *dashboardId, pagingParams.cursor, *sort, pagingParams.search, pagingParams.limit, protocolModes) + data, paging, err := h.getDataAccessor(r).GetValidatorDashboardBlocks(r.Context(), *dashboardId, pagingParams.cursor, *sort, *search, pagingParams.limit, protocolModes) if err != nil { handleErr(w, r, err) return @@ -1559,7 +1585,7 @@ func (h *HandlerService) PublicGetValidatorDashboardExecutionLayerDeposits(w htt handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(r.URL.Query()) + pagingParams := checkPagingParams[types.ELDepositsCursor](&v, r.URL.Query()) if v.hasErrors() { handleErr(w, r, v) return @@ -1595,7 +1621,7 @@ func (h *HandlerService) PublicGetValidatorDashboardConsensusLayerDeposits(w htt handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(r.URL.Query()) + pagingParams := checkPagingParams[types.CLDepositsCursor](&v, r.URL.Query()) if v.hasErrors() { handleErr(w, r, v) return @@ -1692,7 +1718,7 @@ func (h *HandlerService) PublicGetValidatorDashboardWithdrawals(w http.ResponseW handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.WithdrawalsCursor](&v, q) sort := checkSort[enums.VDBWithdrawalsColumn](&v, q.Get("sort")) protocolModes := v.checkProtocolModes(q.Get("modes")) if v.hasErrors() { @@ -1730,7 +1756,7 @@ func (h *HandlerService) PublicGetValidatorDashboardTotalWithdrawals(w http.Resp handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.CursorLike](&v, q) protocolModes := v.checkProtocolModes(q.Get("modes")) if v.hasErrors() { handleErr(w, r, v) @@ -1770,7 +1796,7 @@ func (h *HandlerService) PublicGetValidatorDashboardRocketPool(w http.ResponseWr handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.VDBRocketPoolCursor](&v, q) sort := checkSort[enums.VDBRocketPoolColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) @@ -1806,7 +1832,7 @@ func (h *HandlerService) PublicGetValidatorDashboardTotalRocketPool(w http.Respo handleErr(w, r, err) return } - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.CursorLike](&v, q) if v.hasErrors() { handleErr(w, r, v) return @@ -1848,7 +1874,7 @@ func (h *HandlerService) PublicGetValidatorDashboardRocketPoolMinipools(w http.R } // support ENS names ? nodeAddress := v.checkAddress(vars["node_address"]) - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.VDBRocketPoolMinipoolsCursor](&v, q) sort := checkSort[enums.VDBRocketPoolMinipoolsColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) @@ -1918,7 +1944,7 @@ func (h *HandlerService) PublicGetUserNotificationDashboards(w http.ResponseWrit return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.NotificationsDashboardsCursor](&v, q) sort := checkSort[enums.NotificationDashboardsColumn](&v, q.Get("sort")) chainIds := v.checkNetworksParameter(q.Get("networks")) if v.hasErrors() { @@ -2030,7 +2056,7 @@ func (h *HandlerService) PublicGetUserNotificationMachines(w http.ResponseWriter return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.NotificationMachinesCursor](&v, q) sort := checkSort[enums.NotificationMachinesColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) @@ -2070,7 +2096,7 @@ func (h *HandlerService) PublicGetUserNotificationClients(w http.ResponseWriter, return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.NotificationClientsCursor](&v, q) sort := checkSort[enums.NotificationClientsColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) @@ -2108,7 +2134,7 @@ func (h *HandlerService) PublicGetUserNotificationNetworks(w http.ResponseWriter return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.NotificationNetworksCursor](&v, q) sort := checkSort[enums.NotificationNetworksColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) @@ -2483,7 +2509,7 @@ func (h *HandlerService) PublicGetUserNotificationSettingsDashboards(w http.Resp return } q := r.URL.Query() - pagingParams := v.checkPagingParams(q) + pagingParams := checkPagingParams[types.NotificationSettingsCursor](&v, q) sort := checkSort[enums.NotificationSettingsDashboardColumn](&v, q.Get("sort")) if v.hasErrors() { handleErr(w, r, v) diff --git a/backend/pkg/api/types/data_access.go b/backend/pkg/api/types/data_access.go index 196317e3f..d3408e807 100644 --- a/backend/pkg/api/types/data_access.go +++ b/backend/pkg/api/types/data_access.go @@ -2,9 +2,12 @@ package types import ( "database/sql" + "strconv" + "strings" "time" "github.com/gobitfly/beaconchain/pkg/api/enums" + "github.com/gobitfly/beaconchain/pkg/commons/log" t "github.com/gobitfly/beaconchain/pkg/commons/types" "github.com/gobitfly/beaconchain/pkg/consapi/types" "github.com/gobitfly/beaconchain/pkg/monitoring/constants" @@ -115,6 +118,22 @@ type WithdrawalsCursor struct { Amount uint64 } +type VDBRocketPoolCursor struct { + GenericCursor + + // TODO +} + +type VDBRocketPoolMinipoolsCursor struct { + GenericCursor + + // TODO +} + +type VDBMobileValidatorsCursor struct { + ValidatorsCursor +} + type NotificationSettingsCursor struct { GenericCursor @@ -324,3 +343,166 @@ const CtxUserIdKey CtxKey = "user_id" const CtxIsMockedKey CtxKey = "is_mocked" const CtxMockSeedKey CtxKey = "mock_seed" const CtxDashboardIdKey CtxKey = "dashboard_id" + +// ------------------------- +// Search structs + +// -- General +type SearchType int + +// all possible search types +const ( + SearchTypeInteger SearchType = iota + SearchTypeName + SearchTypeEthereumAddress + SearchTypeWithdrawalCredential + SearchTypeEnsName + SearchTypeGraffiti + SearchTypeEmail + SearchTypePassword + SearchTypeEmailUserToken + SearchTypeJsonContentType + // Validator Dashboard + SearchTypeValidatorDashboardPublicId + SearchTypeValidatorPublicKeyWithPrefix + SearchTypeValidatorPublicKey +) + +type Searchable interface { + GetSearches() []SearchType + SetSearchValue(s string) + SetSearchType(st SearchType) error + HasSearchType(st SearchType) bool + IsEnabled() bool + HasAnyMatches() bool +} + +// not to be used directly, only for embedding +// think of this as a base class which provides default implementations that can be overridden / shadowed / masked (see below) +type basicSearch struct { + types map[SearchType]bool + value string +} + +func (bs *basicSearch) SetSearchValue(s string) { + if bs == nil { + log.Warnf("BasicSearch is nil, can't apply search: %s", s) + return + } + bs.value = s +} + +func (bs *basicSearch) SetSearchType(st SearchType) error { + if bs.types == nil { + bs.types = make(map[SearchType]bool) + } + bs.types[st] = true + return nil +} + +func (bs *basicSearch) HasSearchType(st SearchType) bool { + if bs.types == nil { + return false + } + v, ok := bs.types[st] + if !ok { + return false + } + return v +} + +func (bs *basicSearch) IsEnabled() bool { + return bs != nil && bs.value != "" +} + +func (bs *basicSearch) HasAnyMatches() bool { + for _, v := range bs.types { + if v { + return true + } + } + return false +} + +func (bs *basicSearch) GetSearches() []SearchType { + return []SearchType{ + SearchTypeName, + SearchTypeInteger, + SearchTypeEthereumAddress, + SearchTypeWithdrawalCredential, + SearchTypeEnsName, + SearchTypeGraffiti, + SearchTypeEmail, + SearchTypePassword, + SearchTypeEmailUserToken, + SearchTypeJsonContentType, + SearchTypeValidatorDashboardPublicId, + SearchTypeValidatorPublicKeyWithPrefix, + SearchTypeValidatorPublicKey, + } +} + +type baseSearchResult[T comparable] struct { + Enabled bool + Value T +} + +type SearchNumber = baseSearchResult[uint64] +type SearchString = baseSearchResult[string] + +func (bsr baseSearchResult[T]) Matches(other T) bool { + return !bsr.Filtered(other) +} + +func (bsr baseSearchResult[T]) Filtered(other T) bool { + return bsr.Enabled && bsr.Value != other +} + +// -- Commonly used +type SearchTableByIndexPubkeyGroup struct { + basicSearch + // conditionals + DashboardId VDBId + // matched values + Index SearchNumber + Pubkey SearchString + Group SearchString +} + +func (s SearchTableByIndexPubkeyGroup) SetSearchType(st SearchType) error { + var err error + if err = s.basicSearch.SetSearchType(st); err != nil { + return err + } + if _, ok := s.basicSearch.types[SearchTypeInteger]; ok { + s.Index.Value, err = strconv.ParseUint(s.value, 10, 64) + if err != nil { + return err + } + s.Index.Enabled = true + } + + if s.Pubkey.Enabled = s.HasSearchType(SearchTypeValidatorPublicKeyWithPrefix); s.Pubkey.Enabled { + s.Pubkey.Value = s.value + } + if !s.DashboardId.AggregateGroups && s.DashboardId.Validators == nil { + if s.Group.Enabled = s.HasSearchType(SearchTypeName); s.Group.Enabled { + s.Group.Value = strings.ToLower(s.value) + } + } + return nil +} + +// masked +func (s SearchTableByIndexPubkeyGroup) GetSearches() []SearchType { + return []SearchType{ + SearchTypeInteger, + SearchTypeName, + SearchTypeValidatorPublicKeyWithPrefix, + } +} + +// custom to filter out certain group searches +func (s SearchTableByIndexPubkeyGroup) HasAnyMatches() bool { + return s.Group.Enabled || s.basicSearch.HasAnyMatches() +} diff --git a/backend/pkg/api/types/search.go b/backend/pkg/api/types/search.go index 202b1607e..8e806cb0a 100644 --- a/backend/pkg/api/types/search.go +++ b/backend/pkg/api/types/search.go @@ -37,3 +37,7 @@ type SearchResult struct { type InternalPostSearchResponse struct { Data []SearchResult `json:"data" tstype:"({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[]"` } + +type VDBBlocksSearch = SearchTableByIndexPubkeyGroup +type VDBManageValidatorsSearch = SearchTableByIndexPubkeyGroup +type VDBSummarySearch = SearchTableByIndexPubkeyGroup diff --git a/frontend/types/api/search.ts b/frontend/types/api/search.ts index e5f2eb870..eb01ecd50 100644 --- a/frontend/types/api/search.ts +++ b/frontend/types/api/search.ts @@ -33,3 +33,6 @@ export interface SearchResult { export interface InternalPostSearchResponse { data: ({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[]; } +export type VDBBlocksSearch = SearchTableByIndexPubkeyGroup; +export type VDBManageValidatorsSearch = SearchTableByIndexPubkeyGroup; +export type VDBSummarySearch = SearchTableByIndexPubkeyGroup;