Skip to content

Commit be6f102

Browse files
hasura-botjack-chan-123rakeshkkyVarun-Choudhary
committed
Adding sorting events
GITHUB_PR_NUMBER: 10724 GITHUB_PR_URL: #10724 PR-URL: hasura/graphql-engine-mono#11217 Co-authored-by: jack-chan-123 <[email protected]> Co-authored-by: Rakesh Emmadi <[email protected]> Co-authored-by: Varun Choudhary <[email protected]> GitOrigin-RevId: 61ad5f32b50faac33e60d018f3b012f28e04c48d
1 parent a7b8841 commit be6f102

File tree

10 files changed

+168
-58
lines changed

10 files changed

+168
-58
lines changed

frontend/libs/console/legacy-ce/src/lib/components/Common/FilterQuery/state.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { EventKind } from '../../Services/Events/types';
2525
import { isNotDefined } from '../utils/jsUtils';
2626

2727
const defaultFilter = makeValueFilter('', null, '');
28-
const defaultSort = makeOrderBy('', 'asc');
28+
const defaultSort = makeOrderBy('created_at', 'asc', 'last');
2929
const defaultState = makeFilterState([defaultFilter], [defaultSort], 10, 0);
3030

3131
export type TriggerOperation = 'pending' | 'processed' | 'invocation';
@@ -56,6 +56,7 @@ export const useFilterQuery = (
5656

5757
const offsetValue = isNotDefined(offset) ? state.offset : offset;
5858
const limitValue = isNotDefined(limit) ? state.limit : limit;
59+
const sortsValue = newSorts ?? state.sorts;
5960

6061
let query = {};
6162
const endpoint = endpoints.metadata;
@@ -66,13 +67,16 @@ export const useFilterQuery = (
6667
'one_off',
6768
limitValue ?? 10,
6869
offsetValue ?? 0,
69-
triggerOp
70+
triggerOp,
71+
undefined,
72+
sortsValue
7073
);
7174
} else {
7275
query = getEventInvocations(
7376
'one_off',
7477
limitValue ?? 10,
75-
offsetValue ?? 0
78+
offsetValue ?? 0,
79+
undefined
7680
);
7781
}
7882
} else if (triggerType === 'cron') {
@@ -82,7 +86,8 @@ export const useFilterQuery = (
8286
limitValue ?? 10,
8387
offsetValue ?? 0,
8488
triggerOp,
85-
triggerName
89+
triggerName,
90+
sortsValue
8691
);
8792
} else {
8893
query = getEventInvocations(
@@ -160,8 +165,8 @@ export const useFilterQuery = (
160165
const setter: SetFilterState = {
161166
sorts: (sorts: OrderBy[]) => {
162167
const newSorts = [...sorts];
163-
if (!sorts.length || sorts[sorts.length - 1].column) {
164-
newSorts.push(defaultSort);
168+
if (!sorts.some(s => s.column === 'created_at')) {
169+
newSorts.push(makeOrderBy('created_at', 'asc', 'last'));
165170
}
166171
setState(s => ({
167172
...s,

frontend/libs/console/legacy-ce/src/lib/components/Services/Events/Common/Components/EventsSubTable.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ interface Props extends InjectedReduxProps {
1919
rows: any[];
2020
rowsFormatted: any[];
2121
headings: {
22-
Header: string;
22+
Header: string | React.ReactNode;
2323
accessor: string;
24+
id?: string;
2425
}[];
2526
event: Event;
2627
makeAPICall?: boolean;

frontend/libs/console/legacy-ce/src/lib/components/Services/Events/Common/Components/EventsTable.tsx

+106-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ReactTable, {
44
ComponentPropsGetter0,
55
} from 'react-table';
66
import 'react-table/react-table.css';
7-
import { FaTimes } from 'react-icons/fa';
7+
import { FaTimes, FaSort, FaSortUp, FaSortDown } from 'react-icons/fa';
88
import { Button } from '../../../../../new-components/Button';
99
import { FilterTableProps, GridHeadingProps } from './types';
1010
import { ordinalColSort } from '../../../Data/utils';
@@ -42,6 +42,7 @@ interface Props extends FilterTableProps {
4242
onSuccess: () => void
4343
) => void;
4444
triggerType?: SupportedEvents;
45+
triggerOp?: 'pending' | 'processed' | 'invocation';
4546
}
4647
const EventsTable: React.FC<Props> = props => {
4748
const {
@@ -52,23 +53,27 @@ const EventsTable: React.FC<Props> = props => {
5253
identifier,
5354
onCancelEvent,
5455
triggerType,
56+
triggerOp,
5557
} = props;
5658
const [, setCurrentPage] = React.useState(0);
5759
const [, setPageSize] = React.useState(filterState.limit ?? 10);
5860
const sortedColumns = columns.sort(ordinalColSort);
5961
let shouldSortColumn = true;
62+
63+
// For debugging - log the props to see what's being passed
64+
React.useEffect(() => {
65+
console.log('EventsTable props:', { triggerType, triggerOp });
66+
}, [triggerType, triggerOp]);
6067
const sortByColumn = (col: string) => {
61-
// Remove all the existing order_bys
6268
const existingColSort = filterState.sorts.find(s => s.column === col);
63-
if (existingColSort && existingColSort.type === 'asc') {
64-
runQuery({
65-
sorts: [makeOrderBy(col, 'desc')],
66-
});
67-
} else {
68-
runQuery({
69-
sorts: [makeOrderBy(col, 'asc')],
70-
});
71-
}
69+
const newSort =
70+
existingColSort && existingColSort.type === 'asc'
71+
? makeOrderBy(col, 'desc')
72+
: makeOrderBy(col, 'asc');
73+
74+
runQuery({
75+
sorts: [newSort],
76+
});
7277
};
7378
const changePage = (page: number) => {
7479
if (filterState.offset !== page * filterState.limit) {
@@ -119,20 +124,72 @@ const EventsTable: React.FC<Props> = props => {
119124
const gridHeadings = [expanderActions];
120125
sortedColumns.forEach(column => {
121126
if (column !== 'actions') {
122-
gridHeadings.push({
123-
Header: column,
124-
accessor: column,
125-
});
127+
// Only add sort icons for one-off and cron triggers
128+
// and not for event triggers (data)
129+
if (triggerType && triggerType !== 'data') {
130+
// For debugging - always show sort icons regardless of triggerOp
131+
const existingSort = filterState.sorts.find(s => s.column === column);
132+
133+
// Create a header with the column name and sort icon
134+
const headerContent = (
135+
<div
136+
style={{
137+
display: 'flex',
138+
alignItems: 'center',
139+
justifyContent: 'space-between',
140+
width: '100%',
141+
}}
142+
>
143+
<span>{column}</span>
144+
{existingSort ? (
145+
existingSort.type === 'asc' ? (
146+
<FaSortUp style={{ marginLeft: '5px', fontSize: '16px' }} />
147+
) : (
148+
<FaSortDown style={{ marginLeft: '5px', fontSize: '16px' }} />
149+
)
150+
) : (
151+
<FaSort
152+
style={{ marginLeft: '5px', opacity: 0.5, fontSize: '16px' }}
153+
/>
154+
)}
155+
</div>
156+
);
157+
158+
gridHeadings.push({
159+
Header: headerContent,
160+
accessor: column,
161+
id: column, // Ensure we have an id for the column
162+
});
163+
} else {
164+
// For event triggers or other cases, just use the column name
165+
gridHeadings.push({
166+
Header: column,
167+
accessor: column,
168+
id: column, // Ensure we have an id for the column
169+
});
170+
}
126171
}
127172
});
128173
const invocationColumns = ['http_status', 'id', 'created_at'];
129174
const invocationDataTriggerColumns = ['http_status', 'id', 'created_at'];
130-
const invocationGridHeadings: GridHeadingProps[] = [expanderActions];
175+
const invocationGridHeadings: {
176+
Header: string | React.ReactNode;
177+
accessor: string;
178+
id?: string;
179+
width?: number;
180+
expander?: boolean;
181+
Expander?: React.FC<{
182+
isExpanded: boolean;
183+
viewIndex: number;
184+
}>;
185+
}[] = [expanderActions];
186+
131187
const addToGridHeadings = (headAccArr: string[]) => {
132188
headAccArr.forEach(column => {
133189
invocationGridHeadings.push({
134190
Header: column,
135191
accessor: column,
192+
id: column,
136193
});
137194
});
138195
};
@@ -161,25 +218,41 @@ const EventsTable: React.FC<Props> = props => {
161218
};
162219
});
163220
const getTheadThProps: ComponentPropsGetterC = (
164-
finalState,
165-
some,
221+
_finalState,
222+
_some,
166223
column
167-
) => ({
168-
onClick: () => {
169-
if (
170-
column &&
171-
column.Header &&
172-
shouldSortColumn &&
173-
column.Header !== 'Actions'
174-
) {
175-
sortByColumn(column.Header as string);
176-
}
177-
shouldSortColumn = true;
178-
},
179-
});
224+
) => {
225+
if (!column) {
226+
return {};
227+
}
228+
229+
// Get the column ID or Header text
230+
const columnId =
231+
column.id || (typeof column.Header === 'string' ? column.Header : '');
232+
233+
// Only enable sorting for one-off and cron triggers (not for event triggers)
234+
if (triggerType && triggerType !== 'data') {
235+
return {
236+
onClick: () => {
237+
if (
238+
column.Header &&
239+
shouldSortColumn &&
240+
column.Header !== 'Actions'
241+
) {
242+
sortByColumn(columnId);
243+
}
244+
shouldSortColumn = true;
245+
},
246+
style: { cursor: 'pointer' },
247+
};
248+
}
249+
250+
// For event triggers or other cases, don't add click handler
251+
return {};
252+
};
180253
const getResizerProps: ComponentPropsGetter0 = (
181-
finalState,
182-
none,
254+
_finalState,
255+
_none,
183256
column,
184257
ctx
185258
) => ({

frontend/libs/console/legacy-ce/src/lib/components/Services/Events/Common/Components/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ export type FilterTableProps = {
1515
};
1616

1717
export type GridHeadingProps = {
18-
Header: string;
18+
Header: string | React.ReactNode;
1919
accessor: string;
20+
id?: string;
2021
width?: number;
2122
expander?: boolean;
2223
Expander?: React.FC<{

frontend/libs/console/legacy-ce/src/lib/metadata/queryUtils.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { TriggerOperation } from '../components/Common/FilterQuery/state';
2323
import { isEmpty } from '../components/Common/utils/jsUtils';
2424
import { Nullable } from '../components/Common/utils/tsUtils';
2525
import { getDataSourcePrefix } from './dataSource.utils';
26+
import { OrderBy } from '@hasura/dc-api-types';
2627

2728
export const metadataQueryTypes = [
2829
'add_source',
@@ -813,7 +814,8 @@ export const getEventInvocations = (
813814
type: SupportedEvents,
814815
limit: number,
815816
offset: number,
816-
triggerName?: string // is required for cron
817+
triggerName?: string, // is required for cron
818+
sorts?: OrderBy[]
817819
) => {
818820
const query = {
819821
type: 'get_scheduled_event_invocations',
@@ -837,6 +839,7 @@ export const getEventInvocations = (
837839
...query.args,
838840
limit,
839841
offset,
842+
...(sorts && sorts.length > 0 ? { order_by: sorts } : {}),
840843
get_rows_count: false,
841844
},
842845
};
@@ -847,7 +850,8 @@ export const getScheduledEvents = (
847850
limit: number,
848851
offset: number,
849852
triggerOp: Exclude<TriggerOperation, 'invocation'>,
850-
triggerName?: string // is required for cron triggers
853+
triggerName?: string, // is required for cron triggers
854+
sorts?: any
851855
) => {
852856
const query = {
853857
type: 'get_scheduled_events',
@@ -886,6 +890,7 @@ export const getScheduledEvents = (
886890
limit,
887891
offset,
888892
get_rows_count: false,
893+
...(sorts && sorts.length > 0 ? { order_by: sorts } : {}),
889894
},
890895
};
891896
};

server/src-lib/Hasura/App.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -843,8 +843,8 @@ instance MonadMetadataStorage AppM where
843843
unlockScheduledEvents a b = runInSeparateTx $ unlockScheduledEventsTx a b
844844
unlockAllLockedScheduledEvents = runInSeparateTx unlockAllLockedScheduledEventsTx
845845
clearFutureCronEvents = runInSeparateTx . dropFutureCronEventsTx
846-
getOneOffScheduledEvents a b c = runInSeparateTx $ getOneOffScheduledEventsTx a b c
847-
getCronEvents a b c d = runInSeparateTx $ getCronEventsTx a b c d
846+
getOneOffScheduledEvents a b c d = runInSeparateTx $ getOneOffScheduledEventsTx a b c d
847+
getCronEvents a b c d e = runInSeparateTx $ getCronEventsTx a b c d e
848848
getScheduledEventInvocations a = runInSeparateTx $ getScheduledEventInvocationsTx a
849849
deleteScheduledEvent a b = runInSeparateTx $ deleteScheduledEventTx a b
850850

server/src-lib/Hasura/Backends/Postgres/SQL/DML.hs

+8
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ instance ToSQL OrderByItem where
198198
toSQL (OrderByItem expr ordering nullsOrder) =
199199
toSQL expr <~> toSQL ordering <~> toSQL nullsOrder
200200

201+
instance J.FromJSON OrderByItem where
202+
parseJSON = J.withObject "OrderByItem" $ \o -> do
203+
colText <- o J..: "column"
204+
orderByType <- o J..:? "type"
205+
nulls <- o J..:? "nulls"
206+
let expr = SEUnsafe colText
207+
pure $ OrderByItem expr orderByType nulls
208+
201209
-- | Order by ascending or descending
202210
data OrderType = OTAsc | OTDesc
203211
deriving (Show, Eq, Generic, Data)

server/src-lib/Hasura/Eventing/ScheduledTrigger.hs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1047,16 +1047,20 @@ getOneOffScheduledEventsTx ::
10471047
ScheduledEventPagination ->
10481048
[ScheduledEventStatus] ->
10491049
RowsCountOption ->
1050+
[S.OrderByItem] ->
10501051
PG.TxE QErr (WithOptionalTotalCount [OneOffScheduledEvent])
1051-
getOneOffScheduledEventsTx pagination statuses getRowsCount = do
1052+
getOneOffScheduledEventsTx pagination statuses getRowsCount orderByList = do
10521053
let table = QualifiedObject "hdb_catalog" $ TableName "hdb_scheduled_events"
10531054
statusFilter = mkScheduledEventStatusFilter statuses
1055+
orderByExp = case NE.nonEmpty orderByList of
1056+
Nothing -> scheduledTimeOrderBy
1057+
Just orderByExp' -> S.OrderByExp orderByExp'
10541058
select =
10551059
S.mkSelect
10561060
{ S.selExtr = [S.selectStar],
10571061
S.selFrom = Just $ S.mkSimpleFromExp table,
10581062
S.selWhere = Just $ S.WhereFrag statusFilter,
1059-
S.selOrderBy = Just scheduledTimeOrderBy
1063+
S.selOrderBy = Just orderByExp
10601064
}
10611065
sql = PG.fromBuilder $ toSQL $ mkPaginationSelectExp select pagination getRowsCount
10621066
executeWithOptionalTotalCount sql getRowsCount
@@ -1066,17 +1070,21 @@ getCronEventsTx ::
10661070
ScheduledEventPagination ->
10671071
[ScheduledEventStatus] ->
10681072
RowsCountOption ->
1073+
[S.OrderByItem] ->
10691074
PG.TxE QErr (WithOptionalTotalCount [CronEvent])
1070-
getCronEventsTx triggerName pagination status getRowsCount = do
1075+
getCronEventsTx triggerName pagination status getRowsCount orderByList = do
10711076
let triggerNameFilter =
10721077
S.BECompare S.SEQ (S.SEIdentifier $ Identifier "trigger_name") (S.SELit $ triggerNameToTxt triggerName)
10731078
statusFilter = mkScheduledEventStatusFilter status
1079+
orderByExp = case NE.nonEmpty orderByList of
1080+
Nothing -> scheduledTimeOrderBy
1081+
Just orderByExp' -> S.OrderByExp orderByExp'
10741082
select =
10751083
S.mkSelect
10761084
{ S.selExtr = [S.selectStar],
10771085
S.selFrom = Just $ S.mkSimpleFromExp cronEventsTable,
10781086
S.selWhere = Just $ S.WhereFrag $ S.BEBin S.AndOp triggerNameFilter statusFilter,
1079-
S.selOrderBy = Just scheduledTimeOrderBy
1087+
S.selOrderBy = Just orderByExp
10801088
}
10811089
sql = PG.fromBuilder $ toSQL $ mkPaginationSelectExp select pagination getRowsCount
10821090
executeWithOptionalTotalCount sql getRowsCount

0 commit comments

Comments
 (0)