Skip to content

Commit 29f05d7

Browse files
[Discover] feat: download CSV from discover page (#9530)
* download CSV from discover page Signed-off-by: Justin Kim <jungkm@amazon.com> * Changeset file for PR #9530 created/updated * update tests Signed-off-by: Justin Kim <jungkm@amazon.com> * leverage notificationService for toasts and replace 10,000 string with variable Signed-off-by: Justin Kim <jungkm@amazon.com> * add ability to abort request Signed-off-by: Justin Kim <jungkm@amazon.com> * add null to text type Signed-off-by: Justin Kim <jungkm@amazon.com> * force the text to be a string Signed-off-by: Justin Kim <jungkm@amazon.com> * add expect anything for abortSignal Signed-off-by: Justin Kim <jungkm@amazon.com> * remove test/functional/apps/context Signed-off-by: Justin Kim <jungkm@amazon.com> * Revert "remove test/functional/apps/context" This reverts commit a4ab358. * consolidate some components and separate the SCSS files Signed-off-by: Justin Kim <jungkm@amazon.com> --------- Signed-off-by: Justin Kim <jungkm@amazon.com> Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
1 parent 8eb641e commit 29f05d7

27 files changed

+1800
-21
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ target
2828
/test/*/screenshots/visual_regression_gallery.html
2929
cypress/videos/*
3030
cypress/screenshots/*
31+
cypress/downloads/*
3132
/html_docs
3233
.eslintcache
3334
/data
@@ -73,4 +74,4 @@ snapshots.js
7374
.yarn-local-mirror
7475

7576
# Ignore the generated antlr files
76-
/src/plugins/data/public/antlr/**/grammar/.antlr/
77+
/src/plugins/data/public/antlr/**/grammar/.antlr/

changelogs/fragments/9530.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
feat:
2+
- Add the ability to export to CSV from the discover page ([#9530](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/9530))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Papa from 'papaparse';
7+
import {
8+
getRandomizedWorkspaceName,
9+
setDatePickerDatesAndSearchIfRelevant,
10+
} from '../../../../../../utils/apps/query_enhancements/shared';
11+
import {
12+
DatasetTypes,
13+
DATASOURCE_NAME,
14+
INDEX_WITH_TIME_1,
15+
INDEX_WITHOUT_TIME_1,
16+
QueryLanguages,
17+
} from '../../../../../../utils/apps/query_enhancements/constants';
18+
import {
19+
downloadCsvAndVerify,
20+
generateDownloadCsvTestConfigurations,
21+
getFirstRowForDownloadWithFields,
22+
getFirstRowTimeForSourceDownload,
23+
getHeadersForDownloadWithFields,
24+
getHeadersForSourceDownload,
25+
getMaxCount,
26+
getQueryString,
27+
getVisibleCountForLanguage,
28+
} from '../../../../../../utils/apps/query_enhancements/download_csv';
29+
import { prepareTestSuite } from '../../../../../../utils/helpers';
30+
31+
const workspaceName = getRandomizedWorkspaceName();
32+
33+
const runDownloadCsvTests = () => {
34+
describe('Download as CSV', () => {
35+
before(() => {
36+
cy.osd.setupWorkspaceAndDataSourceWithIndices(workspaceName, [
37+
INDEX_WITH_TIME_1,
38+
INDEX_WITHOUT_TIME_1,
39+
]);
40+
cy.createWorkspaceIndexPatterns({
41+
workspaceName: workspaceName,
42+
indexPattern: INDEX_WITH_TIME_1,
43+
timefieldName: 'timestamp',
44+
dataSource: DATASOURCE_NAME,
45+
isEnhancement: true,
46+
});
47+
cy.createWorkspaceIndexPatterns({
48+
workspaceName: workspaceName,
49+
indexPattern: INDEX_WITHOUT_TIME_1,
50+
timefieldName: '',
51+
dataSource: DATASOURCE_NAME,
52+
isEnhancement: true,
53+
indexPatternHasTimefield: false,
54+
});
55+
});
56+
57+
after(() => {
58+
cy.osd.cleanupWorkspaceAndDataSourceAndIndices(workspaceName, [
59+
INDEX_WITH_TIME_1,
60+
INDEX_WITHOUT_TIME_1,
61+
]);
62+
});
63+
64+
generateDownloadCsvTestConfigurations().forEach((config) => {
65+
it(`should be able to download all CSV options for ${config.saveName}`, () => {
66+
cy.osd.navigateToWorkSpaceSpecificPage({
67+
workspaceName,
68+
page: 'discover',
69+
isEnhancement: true,
70+
});
71+
72+
if (config.datasetType === DatasetTypes.INDEX_PATTERN.name) {
73+
cy.setIndexPatternAsDataset(config.dataset, DATASOURCE_NAME);
74+
} else {
75+
cy.setIndexAsDataset(
76+
config.dataset,
77+
DATASOURCE_NAME,
78+
'PPL',
79+
config.hasTime ? 'timestamp' : "I don't want to use the time filter",
80+
'submit'
81+
);
82+
}
83+
84+
cy.setQueryLanguage(config.language.name);
85+
if (config.hasTime) {
86+
setDatePickerDatesAndSearchIfRelevant(config.language.name);
87+
}
88+
89+
cy.setQueryEditor(getQueryString(config.dataset, config.language.name, config.hasTime), {
90+
parseSpecialCharSequences: false,
91+
});
92+
93+
// waiting as there is no good way to verify that the query has loaded
94+
cy.wait(2000);
95+
96+
// eslint-disable-next-line no-loop-func
97+
downloadCsvAndVerify('Visible', (csvString) => {
98+
const { data } = Papa.parse(csvString);
99+
cy.wrap(data).should(
100+
'have.length',
101+
getVisibleCountForLanguage(config.language, config.hasTime) + 1
102+
);
103+
cy.wrap(data[0]).should('deep.equal', getHeadersForSourceDownload(config.hasTime));
104+
if (config.hasTime) {
105+
cy.wrap(data[1][0]).should('equal', getFirstRowTimeForSourceDownload(config.language));
106+
}
107+
});
108+
109+
if ([QueryLanguages.DQL.name, QueryLanguages.Lucene.name].includes(config.language.name)) {
110+
// eslint-disable-next-line no-loop-func
111+
downloadCsvAndVerify('Max', (csvString) => {
112+
const { data } = Papa.parse(csvString);
113+
cy.wrap(data).should('have.length', getMaxCount(config.hasTime) + 1);
114+
cy.wrap(data[0]).should('deep.equal', getHeadersForSourceDownload(config.hasTime));
115+
if (config.hasTime) {
116+
cy.wrap(data[1][0]).should(
117+
'equal',
118+
getFirstRowTimeForSourceDownload(config.language)
119+
);
120+
}
121+
});
122+
}
123+
124+
// Select some fields
125+
cy.getElementByTestId('fieldToggle-bytes_transferred').click();
126+
cy.getElementByTestId('fieldToggle-personal.name').click();
127+
128+
// eslint-disable-next-line no-loop-func
129+
downloadCsvAndVerify('Visible', (csvString) => {
130+
const { data } = Papa.parse(csvString);
131+
cy.wrap(data).should(
132+
'have.length',
133+
getVisibleCountForLanguage(config.language, config.hasTime) + 1
134+
);
135+
cy.wrap(data[0]).should('deep.equal', getHeadersForDownloadWithFields(config.hasTime));
136+
cy.wrap(data[1]).should(
137+
'deep.equal',
138+
getFirstRowForDownloadWithFields(config.language, config.hasTime)
139+
);
140+
});
141+
142+
if ([QueryLanguages.DQL.name, QueryLanguages.Lucene.name].includes(config.language.name)) {
143+
// eslint-disable-next-line no-loop-func
144+
downloadCsvAndVerify('Max', (csvString) => {
145+
const { data } = Papa.parse(csvString);
146+
cy.wrap(data).should('have.length', getMaxCount(config.hasTime) + 1);
147+
cy.wrap(data[0]).should('deep.equal', getHeadersForDownloadWithFields(config.hasTime));
148+
if (config.hasTime) {
149+
cy.wrap(data[1]).should(
150+
'deep.equal',
151+
getFirstRowForDownloadWithFields(config.language, config.hasTime)
152+
);
153+
}
154+
});
155+
}
156+
157+
// deselect the selected fields
158+
cy.getElementByTestId('fieldToggle-bytes_transferred').click();
159+
cy.getElementByTestId('fieldToggle-personal.name').click();
160+
});
161+
});
162+
163+
it('Should be able to change the number of rows setting and have it download correct amount', () => {
164+
const config = {
165+
dataset: `${INDEX_WITH_TIME_1}*`,
166+
};
167+
const language = QueryLanguages.DQL;
168+
const expectedCount = 95;
169+
170+
cy.visit('/app/settings');
171+
cy.getElementByTestId('settingsSearchBar').should('be.visible').type('Number of rows');
172+
cy.getElementByTestId('advancedSetting-editField-discover:sampleSize')
173+
.clear()
174+
.type(expectedCount.toString())
175+
.type('{rightArrow}{backspace}');
176+
177+
// force: true because sometimes it is hidden by a popup
178+
cy.getElementByTestId('advancedSetting-saveButton').click({ force: true });
179+
180+
cy.getElementByTestId('advancedSetting-saveButton').should('not.exist');
181+
182+
cy.osd.navigateToWorkSpaceSpecificPage({
183+
workspaceName,
184+
page: 'discover',
185+
isEnhancement: true,
186+
});
187+
188+
cy.setIndexPatternAsDataset(config.dataset, DATASOURCE_NAME);
189+
190+
cy.setQueryLanguage(language.name);
191+
setDatePickerDatesAndSearchIfRelevant(language.name);
192+
193+
// eslint-disable-next-line no-loop-func
194+
downloadCsvAndVerify('Visible', (csvString) => {
195+
const { data } = Papa.parse(csvString);
196+
cy.wrap(data).should('have.length', expectedCount + 1);
197+
});
198+
199+
// cleanup
200+
cy.visit('/app/settings');
201+
cy.getElementByTestId('settingsSearchBar').should('be.visible').type('Number of rows');
202+
cy.getElementByTestId('advancedSetting-resetField-discover:sampleSize').click();
203+
204+
// force: true because sometimes it is hidden by a popup
205+
cy.getElementByTestId('advancedSetting-saveButton').click({ force: true });
206+
207+
cy.getElementByTestId('advancedSetting-saveButton').should('not.exist');
208+
});
209+
});
210+
};
211+
212+
prepareTestSuite('Download CSV', runDownloadCsvTests);

0 commit comments

Comments
 (0)