Skip to content
Permalink
Browse files
Segment timeline doesn't show results older than 3 months (#9956)
* Segment timeline doesn't show results older than 3 months

* Adoption testing patch for web segment timeline view and also refactoring default time config
  • Loading branch information
2bethere committed Jun 28, 2020
1 parent a4c6d5f commit 35c7c0ec25405bed6f9a379a135d9cf42bea5d16
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 66 deletions.
@@ -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 = (
<SegmentTimeline capabilities={Capabilities.FULL} chartHeight={100} chartWidth={100} />
);
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 = (
<SegmentTimeline
capabilities={Capabilities.FULL}
chartHeight={100}
chartWidth={100}
dataQueryManager={dataQueryManager}
/>
);
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 = (
<SegmentTimeline
capabilities={Capabilities.FULL}
chartHeight={100}
chartWidth={100}
dataQueryManager={dataQueryManager}
/>
);
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,
});
}
}
@@ -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,81 +233,84 @@ export class SegmentTimeline extends React.PureComponent<
dataToRender: [],
activeDatasource: null,
activeDataType: 'countData',
timeSpan: 3,
timeSpan: DEFAULT_TIME_SPAN_MONTHS,
loading: true,
xScale: null,
yScale: null,
dEnd: dEnd,
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) => {

0 comments on commit 35c7c0e

Please sign in to comment.