diff --git a/dashboards-reports/public/components/report_definitions/report_settings/__tests__/report_settings.test.tsx b/dashboards-reports/public/components/report_definitions/report_settings/__tests__/report_settings.test.tsx
index e61f377a..45447145 100644
--- a/dashboards-reports/public/components/report_definitions/report_settings/__tests__/report_settings.test.tsx
+++ b/dashboards-reports/public/components/report_definitions/report_settings/__tests__/report_settings.test.tsx
@@ -75,6 +75,9 @@ const dashboardHits = {
timeTo: 'now',
title: 'Mock Dashboard',
},
+ notebook: {
+ name: 'mock notebook name'
+ }
},
},
],
@@ -89,6 +92,9 @@ const visualizationHits = {
description: 'mock visualization value',
title: 'Mock Visualization',
},
+ notebook: {
+ name: 'mock notebook name'
+ },
},
},
],
@@ -102,6 +108,9 @@ const savedSearchHits = {
search: {
title: 'Mock saved search value',
},
+ notebook: {
+ name: 'mock notebook name'
+ },
},
},
],
diff --git a/dashboards-reports/public/components/report_definitions/report_settings/report_settings.tsx b/dashboards-reports/public/components/report_definitions/report_settings/report_settings.tsx
index 6fbd3c5d..250569cb 100644
--- a/dashboards-reports/public/components/report_definitions/report_settings/report_settings.tsx
+++ b/dashboards-reports/public/components/report_definitions/report_settings/report_settings.tsx
@@ -65,6 +65,8 @@ import {
getDashboardBaseUrlCreate,
getDashboardOptions,
handleDataToVisualReportSourceChange,
+ getNotebooksOptions,
+ getNotebooksBaseUrlCreate,
} from './report_settings_helpers';
import { TimeRangeSelect } from './time_range';
import { converter } from '../utils';
@@ -110,6 +112,9 @@ export function ReportSettings(props: ReportSettingProps) {
const [savedSearchSourceSelect, setSavedSearchSourceSelect] = useState([] as any);
const [savedSearches, setSavedSearches] = useState([] as any);
+ const [notebooksSourceSelect, setNotebooksSourceSelect] = useState([] as any);
+ const [notebooks, setNotebooks] = useState([] as any);
+
const [fileFormat, setFileFormat] = useState('pdf');
const handleDashboards = (e) => {
@@ -124,6 +129,10 @@ export function ReportSettings(props: ReportSettingProps) {
setSavedSearches(e);
};
+ const handleNotebooks = (e) => {
+ setNotebooks(e);
+ }
+
const handleReportName = (e: {
target: { value: React.SetStateAction };
}) => {
@@ -172,6 +181,15 @@ export function ReportSettings(props: ReportSettingProps) {
reportDefinitionRequest.report_params.core_params.report_format = 'csv';
reportDefinitionRequest.report_params.core_params.limit = 10000;
reportDefinitionRequest.report_params.core_params.excel = true;
+ } else if (e === 'notebooksReportSource') {
+ reportDefinitionRequest.report_params.report_source = 'Notebook';
+ reportDefinitionRequest.report_params.core_params.base_url =
+ getNotebooksBaseUrlCreate(edit, editDefinitionId, fromInContext) +
+ notebooks[0].value;
+
+ // set params to visual report params after switch from saved search
+ handleDataToVisualReportSourceChange(reportDefinitionRequest);
+ setFileFormat('pdf');
}
};
@@ -229,6 +247,22 @@ export function ReportSettings(props: ReportSettingProps) {
}
};
+ const handleNotebooksSelect = (e) => {
+ setNotebooksSourceSelect(e);
+ let fromInContext = false;
+ if (window.location.href.includes('?')) {
+ fromInContext = true;
+ }
+ if (e.length > 0) {
+ reportDefinitionRequest.report_params.core_params.base_url =
+ getNotebooksBaseUrlCreate(edit, editDefinitionId, fromInContext) +
+ e[0].value;
+ }
+ else {
+ reportDefinitionRequest.report_params.core_params.base_url = "";
+ }
+ }
+
const handleFileFormat = (e: React.SetStateAction) => {
setFileFormat(e);
reportDefinitionRequest.report_params.core_params.report_format = e.toString();
@@ -532,6 +566,7 @@ export function ReportSettings(props: ReportSettingProps) {
dashboard: [],
visualizations: [],
savedSearch: [],
+ notebooks: []
};
reportDefinitionRequest.report_params.core_params.report_format = fileFormat;
await httpClientProps
@@ -573,6 +608,17 @@ export function ReportSettings(props: ReportSettingProps) {
.catch((error) => {
console.log('error when fetching saved searches:', error);
});
+
+ await httpClientProps
+ .get('../api/notebooks/')
+ .then(async (response: any) => {
+ let notebooksOptions = getNotebooksOptions(response.data);
+ reportSourceOptions.notebooks = notebooksOptions;
+ await handleNotebooks(notebooksOptions);
+ })
+ .catch((error) => {
+ console.log('error when fetching notebooks:', error);
+ });
return reportSourceOptions;
};
@@ -681,7 +727,41 @@ export function ReportSettings(props: ReportSettingProps) {
);
+ const displayNotebooksSelect =
+ reportSourceId === 'notebooksReportSource' ? (
+
+
+
+
+
+
+ ): null;
+ const displayTimeRangeSelect =
+ reportSourceId != 'notebooksReportSource' ? (
+
+
+
+
+ ): null;
return (
@@ -736,7 +816,7 @@ export function ReportSettings(props: ReportSettingProps) {
{displayDashboardSelect}
{displayVisualizationSelect}
{displaySavedSearchSelect}
-
-
+ */}
+ {displayNotebooksSelect}
+ {displayTimeRangeSelect}
{displayVisualReportsFormatAndMarkdown}
diff --git a/dashboards-reports/public/components/report_definitions/report_settings/report_settings_constants.tsx b/dashboards-reports/public/components/report_definitions/report_settings/report_settings_constants.tsx
index a5532d66..e7d1c750 100644
--- a/dashboards-reports/public/components/report_definitions/report_settings/report_settings_constants.tsx
+++ b/dashboards-reports/public/components/report_definitions/report_settings/report_settings_constants.tsx
@@ -37,6 +37,10 @@ export const REPORT_SOURCE_RADIOS = [
id: 'savedSearchReportSource',
label: 'Saved search',
},
+ {
+ id: 'notebooksReportSource',
+ label: 'Notebook'
+ }
];
export const PDF_PNG_FILE_FORMAT_OPTIONS = [
@@ -75,6 +79,7 @@ export const REPORT_SOURCE_TYPES = {
dashboard: 'Dashboard',
visualization: 'Visualization',
savedSearch: 'Saved search',
+ notebook: 'Notebook'
};
export const commonTimeRanges = [
diff --git a/dashboards-reports/public/components/report_definitions/report_settings/report_settings_helpers.tsx b/dashboards-reports/public/components/report_definitions/report_settings/report_settings_helpers.tsx
index 7c07c4ec..8348fc0e 100644
--- a/dashboards-reports/public/components/report_definitions/report_settings/report_settings_helpers.tsx
+++ b/dashboards-reports/public/components/report_definitions/report_settings/report_settings_helpers.tsx
@@ -111,6 +111,31 @@ export const getSavedSearchBaseUrlCreate = (
);
};
+export const getNotebooksBaseUrlCreate = (
+ edit: boolean,
+ editDefinitionId: string,
+ fromInContext: boolean
+) => {
+ let baseUrl;
+ if (!fromInContext) {
+ baseUrl = location.pathname + location.hash;
+ } else {
+ baseUrl = '/app/notebooks-dashboards#/';
+ }
+ if (edit) {
+ return baseUrl.replace(
+ `reports-dashboards#/edit/${editDefinitionId}`,
+ 'notebooks-dashboards#/'
+ );
+ } else if (fromInContext) {
+ return baseUrl;
+ }
+ return baseUrl.replace(
+ 'reports-dashboards#/create',
+ 'notebooks-dashboards#/'
+ );
+}
+
export const getDashboardOptions = (data) => {
let index;
let dashboard_options = [];
@@ -150,6 +175,19 @@ export const getSavedSearchOptions = (data: string | any[]) => {
return options;
};
+export const getNotebooksOptions = (data: any) => {
+ let index;
+ let options = [];
+ for (index = 0; index < data.length; ++index) {
+ let entry = {
+ value: data[index]['id'],
+ label: data[index]['path']
+ }
+ options.push(entry);
+ }
+ return options;
+}
+
export const handleDataToVisualReportSourceChange = (
reportDefinitionRequest
) => {
diff --git a/dashboards-reports/server/model/backendModel.ts b/dashboards-reports/server/model/backendModel.ts
index 38929735..674eaf74 100644
--- a/dashboards-reports/server/model/backendModel.ts
+++ b/dashboards-reports/server/model/backendModel.ts
@@ -110,6 +110,7 @@ export enum BACKEND_REPORT_SOURCE {
dashboard = 'Dashboard',
visualization = 'Visualization',
savedSearch = 'SavedSearch',
+ notebook = 'Notebook'
}
export enum BACKEND_REPORT_STATE {
@@ -143,6 +144,7 @@ export const REPORT_SOURCE_DICT = {
[REPORT_TYPE.dashboard]: BACKEND_REPORT_SOURCE.dashboard,
[REPORT_TYPE.visualization]: BACKEND_REPORT_SOURCE.visualization,
[REPORT_TYPE.savedSearch]: BACKEND_REPORT_SOURCE.savedSearch,
+ [REPORT_TYPE.notebook]: BACKEND_REPORT_SOURCE.notebook
};
export const REPORT_FORMAT_DICT = {
@@ -166,4 +168,5 @@ export const URL_PREFIX_DICT = {
[BACKEND_REPORT_SOURCE.dashboard]: '/app/dashboards#/view/',
[BACKEND_REPORT_SOURCE.savedSearch]: '/app/discover#/view/',
[BACKEND_REPORT_SOURCE.visualization]: '/app/visualize#/edit/',
+ [BACKEND_REPORT_SOURCE.notebook]: '/app/notebooks-dashboards#/'
};
diff --git a/dashboards-reports/server/model/index.ts b/dashboards-reports/server/model/index.ts
index c807a42c..648f08c9 100644
--- a/dashboards-reports/server/model/index.ts
+++ b/dashboards-reports/server/model/index.ts
@@ -211,6 +211,7 @@ export const reportParamsSchema = schema.object({
schema.literal(REPORT_TYPE.dashboard),
schema.literal(REPORT_TYPE.visualization),
schema.literal(REPORT_TYPE.savedSearch),
+ schema.literal(REPORT_TYPE.notebook)
]),
description: schema.string(),
core_params: schema.conditional(
diff --git a/dashboards-reports/server/routes/lib/createReport.ts b/dashboards-reports/server/routes/lib/createReport.ts
index 819a19c9..3d1f4d27 100644
--- a/dashboards-reports/server/routes/lib/createReport.ts
+++ b/dashboards-reports/server/routes/lib/createReport.ts
@@ -103,7 +103,7 @@ export const createReport = async (
isScheduledTask
);
} else {
- // report source can only be one of [saved search, visualization, dashboard]
+ // report source can only be one of [saved search, visualization, dashboard, notebook]
// compose url
const relativeUrl = report.query_url.startsWith(basePath)
? report.query_url
diff --git a/dashboards-reports/server/routes/reportSource.ts b/dashboards-reports/server/routes/reportSource.ts
index 088191fc..1bdad11d 100644
--- a/dashboards-reports/server/routes/reportSource.ts
+++ b/dashboards-reports/server/routes/reportSource.ts
@@ -73,8 +73,7 @@ export default function (router: IRouter) {
size: DEFAULT_MAX_SIZE,
};
responseParams = params;
- }
- try {
+ } try {
const opensearchResp = await context.core.opensearch.legacy.client.callAsCurrentUser(
'search',
responseParams
diff --git a/dashboards-reports/server/routes/utils/constants.ts b/dashboards-reports/server/routes/utils/constants.ts
index 10e352c7..1b424f95 100644
--- a/dashboards-reports/server/routes/utils/constants.ts
+++ b/dashboards-reports/server/routes/utils/constants.ts
@@ -60,6 +60,7 @@ export enum REPORT_TYPE {
savedSearch = 'Saved search',
dashboard = 'Dashboard',
visualization = 'Visualization',
+ notebook = 'Notebook'
}
export enum DATA_REPORT_CONFIG {
@@ -79,6 +80,7 @@ export enum DELIVERY_TYPE {
export enum SELECTOR {
dashboard = '#dashboardViewport',
visualization = '.visEditor__content',
+ notebook = '.euiPageBody'
}
// https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-request-from-size.html
@@ -167,6 +169,18 @@ export const GLOBAL_BASIC_COUNTER: CountersType = {
},
},
},
+ notebook: {
+ pdf: {
+ download: {
+ count: 0,
+ },
+ },
+ png: {
+ download: {
+ count: 0,
+ },
+ },
+ },
saved_search: {
csv: {
download: {
@@ -262,6 +276,18 @@ export const DEFAULT_ROLLING_COUNTER: CountersType = {
},
},
},
+ notebook: {
+ pdf: {
+ download: {
+ count: 0,
+ },
+ },
+ png: {
+ download: {
+ count: 0,
+ },
+ },
+ },
saved_search: {
csv: {
download: {
diff --git a/dashboards-reports/server/routes/utils/types.ts b/dashboards-reports/server/routes/utils/types.ts
index 3c2cc139..6ba79ebe 100644
--- a/dashboards-reports/server/routes/utils/types.ts
+++ b/dashboards-reports/server/routes/utils/types.ts
@@ -30,7 +30,7 @@ export interface CreateReportResultType {
fileName: string;
}
-type ReportSourceType = 'dashboard' | 'visualization' | 'saved_search';
+type ReportSourceType = 'dashboard' | 'visualization' | 'saved_search' | 'notebook';
type ReportFormatType = 'pdf' | 'png' | 'csv';
type UsageActionType = 'download';
export type EntityType = 'report' | 'report_definition' | 'report_source';
diff --git a/dashboards-reports/server/routes/utils/visual_report/visualReportHelper.ts b/dashboards-reports/server/routes/utils/visual_report/visualReportHelper.ts
index 6cc8f958..5e30dc80 100644
--- a/dashboards-reports/server/routes/utils/visual_report/visualReportHelper.ts
+++ b/dashboards-reports/server/routes/utils/visual_report/visualReportHelper.ts
@@ -169,6 +169,11 @@ export const createVisualReport = async (
visible: true,
});
break;
+ case REPORT_TYPE.notebook:
+ await page.waitForSelector(SELECTOR.notebook, {
+ visible: true,
+ });
+ break;
default:
throw Error(
`report source can only be one of [Dashboard, Visualization]`
diff --git a/dashboards-reports/server/utils/validationHelper.ts b/dashboards-reports/server/utils/validationHelper.ts
index 08990314..937ce150 100644
--- a/dashboards-reports/server/utils/validationHelper.ts
+++ b/dashboards-reports/server/utils/validationHelper.ts
@@ -52,7 +52,7 @@ export const isValidRelativeUrl = (relativeUrl: string) => {
export const regexDuration = /^(-?)P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)([DW]))?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/;
export const regexEmailAddress = /\S+@\S+\.\S+/;
export const regexReportName = /^[\w\-\s\(\)\[\]\,\_\-+]+$/;
-export const regexRelativeUrl = /^\/(_plugin\/kibana\/app|app)\/(dashboards|visualize|discover)(\?security_tenant=.+|)#\/(view|edit)\/[^\/]+$/;
+export const regexRelativeUrl = /^\/(_plugin\/kibana\/app|app)\/(dashboards|visualize|discover|notebooks-dashboards)(\?security_tenant=.+|)#\/(view\/|edit\/)?[^\/]+$/;
export const validateReport = async (
client: ILegacyScopedClusterClient,
@@ -120,16 +120,25 @@ const validateSavedObject = async (
return 'search';
case REPORT_TYPE.visualization:
return 'visualization';
+ case REPORT_TYPE.notebook:
+ return 'notebook';
}
};
- const savedObjectId = `${getType(source)}:${getId(url)}`;
- const params: RequestParams.Exists = {
- index: '.opensearch_dashboards',
- id: savedObjectId,
- };
-
- const exist = await client.callAsCurrentUser('exists', params);
+ let exist = false;
+ let savedObjectId = '';
+ if (getType(source) === 'notebook') {
+ // no backend check for notebooks because we would just be checking against the notebooks api again
+ exist = true;
+ }
+ else {
+ savedObjectId = `${getType(source)}:${getId(url)}`;
+ const params: RequestParams.Exists = {
+ index: '.opensearch_dashboards',
+ id: savedObjectId,
+ };
+ exist = await client.callAsCurrentUser('exists', params);
+ }
if (!exist) {
throw Error(`saved object with id ${savedObjectId} does not exist`);
}
diff --git a/reports-scheduler/src/main/kotlin/com/amazon/opendistroforelasticsearch/reportsscheduler/model/ReportDefinition.kt b/reports-scheduler/src/main/kotlin/com/amazon/opendistroforelasticsearch/reportsscheduler/model/ReportDefinition.kt
index 540d6b04..33fb1533 100644
--- a/reports-scheduler/src/main/kotlin/com/amazon/opendistroforelasticsearch/reportsscheduler/model/ReportDefinition.kt
+++ b/reports-scheduler/src/main/kotlin/com/amazon/opendistroforelasticsearch/reportsscheduler/model/ReportDefinition.kt
@@ -95,7 +95,7 @@ internal data class ReportDefinition(
val delivery: Delivery?
) : ToXContentObject {
- internal enum class SourceType { Dashboard, Visualization, SavedSearch }
+ internal enum class SourceType { Dashboard, Visualization, SavedSearch, Notebook }
internal enum class TriggerType { Download, OnDemand, CronSchedule, IntervalSchedule }
internal enum class DeliveryFormat { LinkOnly, Attachment, Embedded }
internal enum class FileFormat { Pdf, Png, Csv }