diff --git a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
index 9b18d67e68e2..83440d314613 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
@@ -17,18 +17,81 @@
*/
import { render } from '@testing-library/react';
+import { mount } from 'enzyme';
import React from 'react';
+import { QueryManager } from '../../utils';
import { Capabilities } from '../../utils/capabilities';
import { SegmentTimeline } from './segment-timeline';
describe('Segment Timeline', () => {
it('matches snapshot', () => {
- const tableColumn = (
+ const segmentTimeline = (
);
- const { container } = render(tableColumn);
+ const { container } = render(segmentTimeline);
expect(container.firstChild).toMatchSnapshot();
});
+
+ it('queries 3 months of data by default', () => {
+ const dataQueryManager = new MockDataQueryManager();
+ const segmentTimeline = (
+
+ );
+ render(segmentTimeline);
+
+ // Ideally, the test should verify the rendered bar graph to see if the bars
+ // cover the selected period. Since the unit test does not have a druid
+ // instance to query from, just verify the query has the correct time span.
+ expect(dataQueryManager.queryTimeSpan).toBe(3);
+ });
+
+ it('queries matching time span when new period is selected from dropdown', () => {
+ const dataQueryManager = new MockDataQueryManager();
+ const segmentTimeline = (
+
+ );
+ const wrapper = mount(segmentTimeline);
+ const selects = wrapper.find('select');
+ expect(selects.length).toBe(2); // Datasource & Period
+ const periodSelect = selects.at(1);
+ const newTimeSpanMonths = 6;
+ periodSelect.simulate('change', { target: { value: newTimeSpanMonths } });
+
+ // Ideally, the test should verify the rendered bar graph to see if the bars
+ // cover the selected period. Since the unit test does not have a druid
+ // instance to query from, just verify the query has the correct time span.
+ expect(dataQueryManager.queryTimeSpan).toBe(newTimeSpanMonths);
+ });
});
+
+/**
+ * Mock the data query manager, since the unit test does not have a druid instance
+ */
+class MockDataQueryManager extends QueryManager<
+ { capabilities: Capabilities; timeSpan: number },
+ any
+> {
+ queryTimeSpan?: number;
+
+ constructor() {
+ super({
+ processQuery: async ({ timeSpan }) => {
+ this.queryTimeSpan = timeSpan;
+ },
+ debounceIdle: 0,
+ debounceLoading: 0,
+ });
+ }
+}
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx
index 9da1c4c2ee31..e6e7502c9cc0 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -33,6 +33,9 @@ interface SegmentTimelineProps {
capabilities: Capabilities;
chartHeight: number;
chartWidth: number;
+
+ // For testing:
+ dataQueryManager?: QueryManager<{ capabilities: Capabilities; timeSpan: number }, any>;
}
interface SegmentTimelineState {
@@ -81,6 +84,8 @@ interface IntervalRow {
size: number;
}
+const DEFAULT_TIME_SPAN_MONTHS = 3;
+
export class SegmentTimeline extends React.PureComponent<
SegmentTimelineProps,
SegmentTimelineState
@@ -219,7 +224,7 @@ export class SegmentTimeline extends React.PureComponent<
super(props);
const dStart = new Date();
const dEnd = new Date();
- dStart.setMonth(dStart.getMonth() - 3);
+ dStart.setMonth(dStart.getMonth() - DEFAULT_TIME_SPAN_MONTHS);
this.state = {
data: {},
datasources: [],
@@ -228,7 +233,7 @@ export class SegmentTimeline extends React.PureComponent<
dataToRender: [],
activeDatasource: null,
activeDataType: 'countData',
- timeSpan: 3,
+ timeSpan: DEFAULT_TIME_SPAN_MONTHS,
loading: true,
xScale: null,
yScale: null,
@@ -236,73 +241,76 @@ export class SegmentTimeline extends React.PureComponent<
dStart: dStart,
};
- this.dataQueryManager = new QueryManager({
- processQuery: async ({ capabilities, timeSpan }) => {
- let intervals: IntervalRow[];
- let datasources: string[];
- if (capabilities.hasSql()) {
- const query = `SELECT
+ this.dataQueryManager =
+ props.dataQueryManager ||
+ new QueryManager({
+ processQuery: async ({ capabilities, timeSpan }) => {
+ let intervals: IntervalRow[];
+ let datasources: string[];
+ if (capabilities.hasSql()) {
+ const query = `
+SELECT
"start", "end", "datasource",
- COUNT(*) AS "count", SUM("size") as "size"
+COUNT(*) AS "count", SUM("size") as "size"
FROM sys.segments
WHERE "start" > TIME_FORMAT(TIMESTAMPADD(MONTH, -${timeSpan}, CURRENT_TIMESTAMP), 'yyyy-MM-dd''T''hh:mm:ss.SSS')
GROUP BY 1, 2, 3
ORDER BY "start" DESC`;
- intervals = await queryDruidSql({ query });
- datasources = uniq(intervals.map(r => r.datasource));
- } else if (capabilities.hasCoordinatorAccess()) {
- const before = new Date();
- before.setMonth(before.getMonth() - timeSpan);
- const beforeIso = before.toISOString();
-
- datasources = (await axios.get(`/druid/coordinator/v1/datasources`)).data;
- intervals = (await Promise.all(
- datasources.map(async datasource => {
- const intervalMap = (await axios.get(
- `/druid/coordinator/v1/datasources/${datasource}/intervals?simple`,
- )).data;
-
- return Object.keys(intervalMap)
- .map(interval => {
- const [start, end] = interval.split('/');
- const { count, size } = intervalMap[interval];
- return {
- start,
- end,
- datasource,
- count,
- size,
- };
- })
- .filter(a => beforeIso < a.start);
- }),
- ))
- .flat()
- .sort((a, b) => b.start.localeCompare(a.start));
- } else {
- throw new Error(`must have SQL or coordinator access`);
- }
+ intervals = await queryDruidSql({ query });
+ datasources = uniq(intervals.map(r => r.datasource));
+ } else if (capabilities.hasCoordinatorAccess()) {
+ const before = new Date();
+ before.setMonth(before.getMonth() - timeSpan);
+ const beforeIso = before.toISOString();
+
+ datasources = (await axios.get(`/druid/coordinator/v1/datasources`)).data;
+ intervals = (await Promise.all(
+ datasources.map(async datasource => {
+ const intervalMap = (await axios.get(
+ `/druid/coordinator/v1/datasources/${datasource}/intervals?simple`,
+ )).data;
+
+ return Object.keys(intervalMap)
+ .map(interval => {
+ const [start, end] = interval.split('/');
+ const { count, size } = intervalMap[interval];
+ return {
+ start,
+ end,
+ datasource,
+ count,
+ size,
+ };
+ })
+ .filter(a => beforeIso < a.start);
+ }),
+ ))
+ .flat()
+ .sort((a, b) => b.start.localeCompare(a.start));
+ } else {
+ throw new Error(`must have SQL or coordinator access`);
+ }
- const data = SegmentTimeline.processRawData(intervals);
- const stackedData = SegmentTimeline.calculateStackedData(data, datasources);
- const singleDatasourceData = SegmentTimeline.calculateSingleDatasourceData(
- data,
- datasources,
- );
- return { data, datasources, stackedData, singleDatasourceData };
- },
- onStateChange: ({ result, loading, error }) => {
- this.setState({
- data: result ? result.data : undefined,
- datasources: result ? result.datasources : [],
- stackedData: result ? result.stackedData : undefined,
- singleDatasourceData: result ? result.singleDatasourceData : undefined,
- loading,
- error,
- });
- },
- });
+ const data = SegmentTimeline.processRawData(intervals);
+ const stackedData = SegmentTimeline.calculateStackedData(data, datasources);
+ const singleDatasourceData = SegmentTimeline.calculateSingleDatasourceData(
+ data,
+ datasources,
+ );
+ return { data, datasources, stackedData, singleDatasourceData };
+ },
+ onStateChange: ({ result, loading, error }) => {
+ this.setState({
+ data: result ? result.data : undefined,
+ datasources: result ? result.datasources : [],
+ stackedData: result ? result.stackedData : undefined,
+ singleDatasourceData: result ? result.singleDatasourceData : undefined,
+ loading,
+ error,
+ });
+ },
+ });
}
componentDidMount(): void {
@@ -394,14 +402,16 @@ ORDER BY "start" DESC`;
onTimeSpanChange = (e: any) => {
const dStart = new Date();
const dEnd = new Date();
- dStart.setMonth(dStart.getMonth() - e);
+ const capabilities = this.props.capabilities;
+ const timeSpan = parseInt(e, 10) || DEFAULT_TIME_SPAN_MONTHS;
+ dStart.setMonth(dStart.getMonth() - timeSpan);
this.setState({
timeSpan: e,
loading: true,
dStart,
dEnd,
});
- this.dataQueryManager.rerunLastQuery();
+ this.dataQueryManager.runQuery({ capabilities, timeSpan });
};
formatTick = (n: number) => {