diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts index f9bf31ad35f6f..a07830f9b9bad 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts @@ -34,12 +34,6 @@ export interface ElementPosition { }; } -export interface Viewport { - zoom: number; - width: number; - height: number; -} - interface OpenOptions { context?: Context; headers: Headers; @@ -156,7 +150,7 @@ export class HeadlessChromiumDriver { } /* - * Call Page.screenshot and return a base64-encoded string of the image + * Receive a PNG buffer of the page screenshot from Chromium */ async screenshot(elementPosition: ElementPosition): Promise { const { boundingClientRect, scroll } = elementPosition; @@ -167,6 +161,7 @@ export class HeadlessChromiumDriver { height: boundingClientRect.height, width: boundingClientRect.width, }, + captureBeyondViewport: false, // workaround for an internal resize. See: https://github.com/puppeteer/puppeteer/issues/7043 }); if (Buffer.isBuffer(screenshot)) { @@ -216,14 +211,18 @@ export class HeadlessChromiumDriver { await this.page.waitForFunction(fn, { timeout, polling: WAIT_FOR_DELAY_MS }, ...args); } + /** + * Setting the viewport is required to ensure that all capture elements are visible: anything not in the + * viewport can not be captured. + */ async setViewport( - { width: _width, height: _height, zoom }: Viewport, + { width: _width, height: _height, zoom }: { zoom: number; width: number; height: number }, logger: Logger ): Promise { const width = Math.floor(_width); const height = Math.floor(_height); - logger.debug(`Setting viewport to: width=${width} height=${height} zoom=${zoom}`); + logger.debug(`Setting viewport to: width=${width} height=${height} scaleFactor=${zoom}`); await this.page.setViewport({ width, diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts index 5c9136b272831..df0d3595f4df2 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts @@ -10,8 +10,8 @@ import * as Rx from 'rxjs'; import { mergeMap, take } from 'rxjs/operators'; import type { Logger } from 'src/core/server'; import type { ScreenshotModePluginSetup } from 'src/plugins/screenshot_mode/server'; +import { DEFAULT_VIEWPORT, HeadlessChromiumDriverFactory } from '.'; import { ConfigType } from '../../../config'; -import { HeadlessChromiumDriverFactory, DEFAULT_VIEWPORT } from '.'; jest.mock('puppeteer'); diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts index 27bb4e79374f7..b1c40566c1093 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts @@ -7,20 +7,19 @@ import { getDataPath } from '@kbn/utils'; import { spawn } from 'child_process'; -import _ from 'lodash'; import del from 'del'; import fs from 'fs'; import { uniq } from 'lodash'; import path from 'path'; -import puppeteer, { Browser, ConsoleMessage, HTTPRequest, Page } from 'puppeteer'; +import puppeteer, { Browser, ConsoleMessage, HTTPRequest, Page, Viewport } from 'puppeteer'; import { createInterface } from 'readline'; import * as Rx from 'rxjs'; import { InnerSubscriber } from 'rxjs/internal/InnerSubscriber'; import { catchError, + concatMap, ignoreElements, map, - concatMap, mergeMap, reduce, takeUntil, @@ -28,9 +27,9 @@ import { } from 'rxjs/operators'; import type { Logger } from 'src/core/server'; import type { ScreenshotModePluginSetup } from 'src/plugins/screenshot_mode/server'; -import { ConfigType } from '../../../config'; -import { errors } from '../../../../common'; import { getChromiumDisconnectedError } from '../'; +import { errors } from '../../../../common'; +import { ConfigType } from '../../../config'; import { safeChildProcess } from '../../safe_child_process'; import { HeadlessChromiumDriver } from '../driver'; import { args } from './args'; @@ -38,12 +37,7 @@ import { getMetrics, PerformanceMetrics } from './metrics'; interface CreatePageOptions { browserTimezone?: string; - defaultViewport: { - /** Size in pixels */ - width?: number; - /** Size in pixels */ - height?: number; - }; + defaultViewport: { width?: number }; openUrlTimeout: number; } @@ -64,10 +58,16 @@ interface ClosePageResult { metrics?: PerformanceMetrics; } -export const DEFAULT_VIEWPORT = { - width: 1950, - height: 1200, -}; +/** + * Size of the desired initial viewport. This is needed to render the app before elements load into their + * layout. Once the elements are positioned, the viewport must be *resized* to include the entire element container. + */ +export const DEFAULT_VIEWPORT: Required> = + { + width: 1950, + height: 1200, + deviceScaleFactor: 1, + }; // Default args used by pptr // https://github.com/puppeteer/puppeteer/blob/13ea347/src/node/Launcher.ts#L168 @@ -142,6 +142,17 @@ export class HeadlessChromiumDriverFactory { const logger = pLogger.get('browser-driver'); logger.info(`Creating browser page driver`); + // We set the viewport width using the client-side layout info to reduce the chances of + // browser reflow. Only the window height is expected to be adjusted dramatically + // before taking a screenshot, to ensure the elements to capture are contained in the viewport. + const viewport = { + ...DEFAULT_VIEWPORT, + width: defaultViewport.width ?? DEFAULT_VIEWPORT.width, + }; + + logger.debug( + `Launching with viewport: width=${viewport.width} height=${viewport.height} scaleFactor=${viewport.deviceScaleFactor}` + ); const chromiumArgs = this.getChromiumArgs(); logger.debug(`Chromium launch args set to: ${chromiumArgs}`); @@ -154,13 +165,7 @@ export class HeadlessChromiumDriverFactory { ignoreHTTPSErrors: true, handleSIGHUP: false, args: chromiumArgs, - - // We optionally set this at page creation to reduce the chances of - // browser reflow. In most cases only the height needs to be adjusted - // before taking a screenshot. - // NOTE: _.defaults assigns to the target object, so we copy it. - // NOTE NOTE: _.defaults is not the same as { ...DEFAULT_VIEWPORT, ...defaultViewport } - defaultViewport: _.defaults({ ...defaultViewport }, DEFAULT_VIEWPORT), + defaultViewport: viewport, env: { TZ: browserTimezone, }, diff --git a/x-pack/plugins/screenshotting/server/browsers/mock.ts b/x-pack/plugins/screenshotting/server/browsers/mock.ts index 9904f3e396830..028bc6fc439d7 100644 --- a/x-pack/plugins/screenshotting/server/browsers/mock.ts +++ b/x-pack/plugins/screenshotting/server/browsers/mock.ts @@ -6,16 +6,17 @@ */ import { NEVER, of } from 'rxjs'; -import type { HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from './chromium'; import { - CONTEXT_SKIPTELEMETRY, + CONTEXT_DEBUG, + CONTEXT_ELEMENTATTRIBUTES, CONTEXT_GETNUMBEROFITEMS, + CONTEXT_GETRENDERERRORS, + CONTEXT_GETTIMERANGE, CONTEXT_INJECTCSS, + CONTEXT_SKIPTELEMETRY, CONTEXT_WAITFORRENDER, - CONTEXT_GETTIMERANGE, - CONTEXT_ELEMENTATTRIBUTES, - CONTEXT_GETRENDERERRORS, } from '../screenshots/constants'; +import type { HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from './chromium'; const selectors = { renderComplete: 'renderedSelector', @@ -40,6 +41,7 @@ function getElementsPositionAndAttributes(title: string, description: string) { export function createMockBrowserDriver(): jest.Mocked { const evaluate = jest.fn(async (_, { context }) => { switch (context) { + case CONTEXT_DEBUG: case CONTEXT_SKIPTELEMETRY: case CONTEXT_INJECTCSS: case CONTEXT_WAITFORRENDER: diff --git a/x-pack/plugins/screenshotting/server/layouts/base_layout.ts b/x-pack/plugins/screenshotting/server/layouts/base_layout.ts index 846904170a0c1..e713c4c3cdcf2 100644 --- a/x-pack/plugins/screenshotting/server/layouts/base_layout.ts +++ b/x-pack/plugins/screenshotting/server/layouts/base_layout.ts @@ -48,9 +48,16 @@ export abstract class BaseLayout { pageSizeParams: PageSizeParams ): CustomPageSize | PredefinedPageSize; - // Return the dimensions unscaled dimensions (before multiplying the zoom factor) - // driver.setViewport() Adds a top and left margin to the viewport, and then multiplies by the scaling factor - public abstract getViewport(itemsCount: number): ViewZoomWidthHeight | null; + /** + * Return the unscaled dimensions (before multiplying the zoom factor) + * + * `itemsCount` is only needed for the `print` layout implementation, where the number of items to capture + * affects the viewport size + * + * @param {number} [itemsCount=1] - The number of items to capture. Default is 1. + * @returns ViewZoomWidthHeight - Viewport data + */ + public abstract getViewport(itemsCount?: number): ViewZoomWidthHeight | null; public abstract getBrowserZoom(): number; diff --git a/x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts b/x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts index 1ea6c7440b455..55b93ed534100 100644 --- a/x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts +++ b/x-pack/plugins/screenshotting/server/layouts/create_layout.test.ts @@ -61,6 +61,7 @@ describe('Create Layout', () => { }, "useReportingBranding": true, "viewport": Object { + "deviceScaleFactor": 1, "height": 1200, "width": 1950, }, diff --git a/x-pack/plugins/screenshotting/server/layouts/print_layout.ts b/x-pack/plugins/screenshotting/server/layouts/print_layout.ts index bfcbe84842c40..e9beb2821b11b 100644 --- a/x-pack/plugins/screenshotting/server/layouts/print_layout.ts +++ b/x-pack/plugins/screenshotting/server/layouts/print_layout.ts @@ -6,10 +6,10 @@ */ import { PageOrientation, PredefinedPageSize } from 'pdfmake/interfaces'; -import type { LayoutParams, LayoutSelectorDictionary } from '../../common/layout'; -import { LayoutTypes } from '../../common'; import type { Layout } from '.'; import { DEFAULT_SELECTORS } from '.'; +import { LayoutTypes } from '../../common'; +import type { LayoutParams, LayoutSelectorDictionary } from '../../common/layout'; import { DEFAULT_VIEWPORT } from '../browsers'; import { BaseLayout } from './base_layout'; @@ -40,7 +40,7 @@ export class PrintLayout extends BaseLayout implements Layout { return this.zoom; } - public getViewport(itemsCount: number) { + public getViewport(itemsCount = 1) { return { zoom: this.zoom, width: this.viewport.width, diff --git a/x-pack/plugins/screenshotting/server/screenshots/constants.ts b/x-pack/plugins/screenshotting/server/screenshots/constants.ts index b1064ec147745..63ccb479d3951 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/constants.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/constants.ts @@ -17,3 +17,4 @@ export const CONTEXT_ELEMENTATTRIBUTES = 'ElementPositionAndAttributes'; export const CONTEXT_WAITFORELEMENTSTOBEINDOM = 'WaitForElementsToBeInDOM'; export const CONTEXT_SKIPTELEMETRY = 'SkipTelemetry'; export const CONTEXT_READMETADATA = 'ReadVisualizationsMetadata'; +export const CONTEXT_DEBUG = 'Debug'; diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts index 7d5791f0dfeb1..7537f2608986d 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.test.ts @@ -28,11 +28,12 @@ describe('getElementPositionAndAttributes', () => { elements.forEach((element) => Object.assign(element, { + scrollIntoView: () => {}, getBoundingClientRect: () => ({ width: parseFloat(element.style.width), height: parseFloat(element.style.height), - top: parseFloat(element.style.top), - left: parseFloat(element.style.left), + y: parseFloat(element.style.top), + x: parseFloat(element.style.left), }), }) ); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts index 0e2106a98d8b0..8dc090dea1865 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_element_position_data.ts @@ -54,9 +54,8 @@ export const getElementPositionAndAttributes = async ( results.push({ position: { boundingClientRect: { - // modern browsers support x/y, but older ones don't - top: boundingClientRect.y || boundingClientRect.top, - left: boundingClientRect.x || boundingClientRect.left, + top: boundingClientRect.y, + left: boundingClientRect.x, width: boundingClientRect.width, height: boundingClientRect.height, }, diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts index 2bb00413c8231..de53972fe7ba9 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts @@ -7,6 +7,8 @@ import type { Logger } from 'src/core/server'; import { createMockBrowserDriver } from '../browsers/mock'; +import { Layout } from '../layouts'; +import { createMockLayout } from '../layouts/mock'; import { getScreenshots } from './get_screenshots'; describe('getScreenshots', () => { @@ -28,12 +30,14 @@ describe('getScreenshots', () => { ]; let browser: ReturnType; let logger: jest.Mocked; + let layout: Layout; beforeEach(async () => { browser = createMockBrowserDriver(); logger = { info: jest.fn() } as unknown as jest.Mocked; browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); + layout = createMockLayout(); }); afterEach(() => { @@ -41,7 +45,7 @@ describe('getScreenshots', () => { }); it('should return screenshots', async () => { - await expect(getScreenshots(browser, logger, elementsPositionAndAttributes)).resolves + await expect(getScreenshots(browser, logger, elementsPositionAndAttributes, layout)).resolves .toMatchInlineSnapshot(` Array [ Object { @@ -87,7 +91,7 @@ describe('getScreenshots', () => { }); it('should forward elements positions', async () => { - await getScreenshots(browser, logger, elementsPositionAndAttributes); + await getScreenshots(browser, logger, elementsPositionAndAttributes, layout); expect(browser.screenshot).toHaveBeenCalledTimes(2); expect(browser.screenshot).toHaveBeenNthCalledWith( @@ -104,7 +108,7 @@ describe('getScreenshots', () => { browser.screenshot.mockResolvedValue(Buffer.from('')); await expect( - getScreenshots(browser, logger, elementsPositionAndAttributes) + getScreenshots(browser, logger, elementsPositionAndAttributes, layout) ).rejects.toBeInstanceOf(Error); }); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts index 26ef272e7f18e..3e015bb89a3ff 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts @@ -8,6 +8,7 @@ import apm from 'elastic-apm-node'; import type { Logger } from 'src/core/server'; import type { HeadlessChromiumDriver } from '../browsers'; +import { Layout } from '../layouts'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; export interface Screenshot { @@ -27,10 +28,52 @@ export interface Screenshot { description: string | null; } +/** + * Resize the viewport to contain the element to capture. + * + * @async + * @param {HeadlessChromiumDriver} browser - used for its methods to control the page + * @param {ElementsPositionAndAttribute['position']} position - position data for the element to capture + * @param {Layout} layout - used for client-side layout data from the job params + * @param {Logger} logger + */ +const resizeViewport = async ( + browser: HeadlessChromiumDriver, + position: ElementsPositionAndAttribute['position'], + layout: Layout, + logger: Logger +) => { + const { boundingClientRect, scroll } = position; + + // Using width from the layout is preferred, it avoids the elements moving around horizontally, + // which would invalidate the position data that was passed in. + const width = layout.width || boundingClientRect.left + scroll.x + boundingClientRect.width; + + await browser.setViewport( + { + width, + height: boundingClientRect.top + scroll.y + boundingClientRect.height, + zoom: layout.getBrowserZoom(), + }, + logger + ); +}; + +/** + * Get screenshots of multiple areas of the page + * + * @async + * @param {HeadlessChromiumDriver} browser - used for its methods to control the page + * @param {Logger} logger + * @param {ElementsPositionAndAttribute[]} elementsPositionAndAttributes[] - position data about all the elements to capture + * @param {Layout} layout - used for client-side layout data from the job params + * @returns {Promise} + */ export const getScreenshots = async ( browser: HeadlessChromiumDriver, logger: Logger, - elementsPositionAndAttributes: ElementsPositionAndAttribute[] + elementsPositionAndAttributes: ElementsPositionAndAttribute[], + layout: Layout ): Promise => { logger.info(`taking screenshots`); @@ -40,6 +83,8 @@ export const getScreenshots = async ( const span = apm.startSpan('get_screenshots', 'read'); const item = elementsPositionAndAttributes[i]; + await resizeViewport(browser, item.position, layout, logger); + const data = await browser.screenshot(item.position); if (!data?.byteLength) { diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts index 235e034fb777b..8941360d73b03 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { KibanaRequest, Logger } from 'src/core/server'; import apm from 'elastic-apm-node'; -import { from, of, Observable } from 'rxjs'; +import { from, Observable, of } from 'rxjs'; import { catchError, concatMap, @@ -18,18 +19,16 @@ import { tap, toArray, } from 'rxjs/operators'; -import type { KibanaRequest, Logger } from 'src/core/server'; import { LayoutParams } from '../../common'; -import type { ConfigType } from '../config'; import type { HeadlessChromiumDriverFactory, PerformanceMetrics } from '../browsers'; -import { createLayout } from '../layouts'; +import type { ConfigType } from '../config'; import type { Layout } from '../layouts'; -import { ScreenshotObservableHandler } from './observable'; +import { createLayout } from '../layouts'; import type { ScreenshotObservableOptions, ScreenshotObservableResult } from './observable'; +import { ScreenshotObservableHandler } from './observable'; import { Semaphore } from './semaphore'; -export type { UrlOrUrlWithContext } from './observable'; -export type { ScreenshotObservableResult } from './observable'; +export type { ScreenshotObservableResult, UrlOrUrlWithContext } from './observable'; export interface ScreenshotOptions extends ScreenshotObservableOptions { @@ -101,7 +100,7 @@ export class Screenshots { { browserTimezone, openUrlTimeout, - defaultViewport: { height: layout.height, width: layout.width }, + defaultViewport: { width: layout.width }, }, this.logger ) diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.ts index fbc147102e0af..a64b18098df52 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.ts @@ -6,19 +6,19 @@ */ import type { Transaction } from 'elastic-apm-node'; -import { defer, forkJoin, throwError, Observable } from 'rxjs'; +import { defer, forkJoin, Observable, throwError } from 'rxjs'; import { catchError, mergeMap, switchMapTo, timeoutWith } from 'rxjs/operators'; import type { Headers, Logger } from 'src/core/server'; import { errors } from '../../common'; import type { Context, HeadlessChromiumDriver } from '../browsers'; -import { getChromiumDisconnectedError, DEFAULT_VIEWPORT } from '../browsers'; +import { DEFAULT_VIEWPORT, getChromiumDisconnectedError } from '../browsers'; import type { Layout } from '../layouts'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; import { getElementPositionAndAttributes } from './get_element_position_data'; import { getNumberOfItems } from './get_number_of_items'; import { getRenderErrors } from './get_render_errors'; -import { getScreenshots } from './get_screenshots'; import type { Screenshot } from './get_screenshots'; +import { getScreenshots } from './get_screenshots'; import { getTimeRange } from './get_time_range'; import { injectCustomCss } from './inject_css'; import { openUrl } from './open_url'; @@ -126,15 +126,6 @@ const getDefaultElementPosition = (dimensions: { height?: number; width?: number ]; }; -/* - * If Kibana is showing a non-HTML error message, the viewport might not be - * provided by the browser. - */ -const getDefaultViewPort = () => ({ - ...DEFAULT_VIEWPORT, - zoom: 1, -}); - export class ScreenshotObservableHandler { constructor( private readonly driver: HeadlessChromiumDriver, @@ -189,15 +180,9 @@ export class ScreenshotObservableHandler { const waitTimeout = this.options.timeouts.waitForElements; return defer(() => getNumberOfItems(driver, this.logger, waitTimeout, this.layout)).pipe( - mergeMap(async (itemsCount) => { - // set the viewport to the dimensions from the job, to allow elements to flow into the expected layout - const viewport = this.layout.getViewport(itemsCount) || getDefaultViewPort(); - - // Set the viewport allowing time for the browser to handle reflow and redraw - // before checking for readiness of visualizations. - await driver.setViewport(viewport, this.logger); - await waitForVisualizations(driver, this.logger, waitTimeout, itemsCount, this.layout); - }), + mergeMap((itemsCount) => + waitForVisualizations(driver, this.logger, waitTimeout, itemsCount, this.layout) + ), this.waitUntil(waitTimeout, 'wait for elements') ); } @@ -244,10 +229,10 @@ export class ScreenshotObservableHandler { this.checkPageIsOpen(); // fail the report job if the browser has closed const elements = data.elementsPositionAndAttributes ?? - getDefaultElementPosition(this.layout.getViewport(1)); + getDefaultElementPosition(this.layout.getViewport()); let screenshots: Screenshot[] = []; try { - screenshots = await getScreenshots(this.driver, this.logger, elements); + screenshots = await getScreenshots(this.driver, this.logger, elements, this.layout); } catch (e) { throw new errors.FailedToCaptureScreenshot(e.message); } diff --git a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts b/x-pack/plugins/screenshotting/server/screenshots/open_url.ts index 8b26f35961db4..0284d57be535b 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/open_url.ts @@ -7,9 +7,8 @@ import apm from 'elastic-apm-node'; import type { Headers, Logger } from 'src/core/server'; -import type { HeadlessChromiumDriver } from '../browsers'; -import type { Context } from '../browsers'; -import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; +import type { Context, HeadlessChromiumDriver } from '../browsers'; +import { CONTEXT_DEBUG, DEFAULT_PAGELOAD_SELECTOR } from './constants'; export const openUrl = async ( browser: HeadlessChromiumDriver, @@ -28,6 +27,27 @@ export const openUrl = async ( try { await browser.open(url, { context, headers, waitForSelector, timeout }, logger); + + // Debug logging for viewport size and resizing + await browser.evaluate( + { + fn() { + // eslint-disable-next-line no-console + console.log( + `Navigating URL with viewport size: width=${window.innerWidth} height=${window.innerHeight} scaleFactor:${window.devicePixelRatio}` + ); + window.addEventListener('resize', () => { + // eslint-disable-next-line no-console + console.log( + `Detected a viewport resize: width=${window.innerWidth} height=${window.innerHeight} scaleFactor:${window.devicePixelRatio}` + ); + }); + }, + args: [], + }, + { context: CONTEXT_DEBUG }, + logger + ); } catch (err) { logger.error(err); throw new Error(`An error occurred when trying to open the Kibana URL: ${err.message}`); diff --git a/x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/sample_data_ecommerce_76.png b/x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/sample_data_ecommerce_76.png new file mode 100644 index 0000000000000..34acd46be2ca9 Binary files /dev/null and b/x-pack/test/functional/apps/dashboard/group1/reporting/reports/baseline/sample_data_ecommerce_76.png differ diff --git a/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/sample_data_ecommerce_76.png b/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/sample_data_ecommerce_76.png new file mode 100644 index 0000000000000..bf2f54076342c Binary files /dev/null and b/x-pack/test/functional/apps/dashboard/reporting/reports/baseline/sample_data_ecommerce_76.png differ diff --git a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts index d42d23b7578a5..5eb384a035978 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts @@ -24,14 +24,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const reporting = getService('reporting'); const ecommerceSOPath = 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce.json'; + const loadEcommerce = async () => { + await esArchiver.load('x-pack/test/functional/es_archives/reporting/ecommerce'); + await kibanaServer.importExport.load(ecommerceSOPath); + await kibanaServer.uiSettings.replace({ + defaultIndex: '5193f870-d861-11e9-a311-0fa548c5f953', + }); + }; + const unloadEcommerce = async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); + await kibanaServer.importExport.unload(ecommerceSOPath); + }; + describe('Dashboard Reporting Screenshots', () => { before('initialize tests', async () => { - await kibanaServer.uiSettings.replace({ - defaultIndex: '5193f870-d861-11e9-a311-0fa548c5f953', - }); - - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/reporting/ecommerce'); - await kibanaServer.importExport.load(ecommerceSOPath); + await loadEcommerce(); await browser.setWindowSize(1600, 850); await security.role.create('test_dashboard_user', { @@ -39,7 +46,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { cluster: [], indices: [ { - names: ['ecommerce'], + names: ['ecommerce', 'kibana_sample_data_ecommerce'], privileges: ['read'], field_security: { grant: ['*'], except: [] }, }, @@ -61,8 +68,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]); }); after('clean up archives', async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); - await kibanaServer.importExport.unload(ecommerceSOPath); + await unloadEcommerce(); await es.deleteByQuery({ index: '.reporting-*', refresh: true, @@ -88,6 +94,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('Print Layout', () => { + before(async () => { + await loadEcommerce(); + }); + after(async () => { + await unloadEcommerce(); + }); + it('downloads a PDF file', async function () { // Generating and then comparing reports can take longer than the default 60s timeout because the comparePngs // function is taking about 15 seconds per comparison in jenkins. @@ -107,6 +120,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('Print PNG button', () => { + before(async () => { + await loadEcommerce(); + }); + after(async () => { + await unloadEcommerce(); + }); + it('is available if new', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); @@ -123,7 +143,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('PNG Layout', () => { - it('downloads a PNG file: small dashboard', async function () { + before(async () => { + await loadEcommerce(); + }); + after(async () => { + await unloadEcommerce(); + }); + + it('PNG file matches the baseline: small dashboard', async function () { this.timeout(300000); await PageObjects.common.navigateToApp('dashboard'); @@ -152,7 +179,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(percentDiff).to.be.lessThan(0.09); }); - it('downloads a PNG file: large dashboard', async function () { + it('PNG file matches the baseline: large dashboard', async function () { this.timeout(300000); await PageObjects.common.navigateToApp('dashboard'); @@ -183,6 +210,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('Preserve Layout', () => { + before(async () => { + await loadEcommerce(); + }); + after(async () => { + await unloadEcommerce(); + }); + it('downloads a PDF file: small dashboard', async function () { this.timeout(300000); await PageObjects.common.navigateToApp('dashboard'); @@ -227,5 +261,57 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await kibanaServer.uiSettings.replace({}); }); }); + + describe('Sample data from Kibana 7.6', () => { + const reportFileName = 'sample_data_ecommerce_76'; + let sessionReportPath: string; + + before(async () => { + await kibanaServer.uiSettings.replace({ + defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + }); + + await esArchiver.load('x-pack/test/functional/es_archives/reporting/ecommerce_76'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json' + ); + + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('[K7.6-eCommerce] Revenue Dashboard'); + + await PageObjects.reporting.openPngReportingPanel(); + await PageObjects.reporting.forceSharedItemsContainerSize({ width: 1405 }); + await PageObjects.reporting.clickGenerateReportButton(); + await PageObjects.reporting.removeForceSharedItemsContainerSize(); + + const url = await PageObjects.reporting.getReportURL(60000); + const reportData = await PageObjects.reporting.getRawPdfReportData(url); + sessionReportPath = await PageObjects.reporting.writeSessionReport( + reportFileName, + 'png', + reportData, + REPORTS_FOLDER + ); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce_76'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json' + ); + }); + + it('PNG file matches the baseline image', async function () { + this.timeout(300000); + const percentDiff = await reporting.checkIfPngsMatch( + sessionReportPath, + PageObjects.reporting.getBaselineReportPath(reportFileName, 'png', REPORTS_FOLDER), + config.get('screenshots.directory'), + log + ); + + expect(percentDiff).to.be.lessThan(0.09); + }); + }); }); } diff --git a/x-pack/test/functional/apps/maps/reports/index.ts b/x-pack/test/functional/apps/maps/reports/index.ts index 4e942b1e150ef..0102b22efac5d 100644 --- a/x-pack/test/functional/apps/maps/reports/index.ts +++ b/x-pack/test/functional/apps/maps/reports/index.ts @@ -16,7 +16,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const log = getService('log'); const reporting = getService('reporting'); - describe('dashboard reporting', () => { + describe('dashboard reporting: creates a map report', () => { // helper function to check the difference between the new image and the baseline const measurePngDifference = async (fileName: string) => { const url = await PageObjects.reporting.getReportURL(60000); @@ -43,7 +43,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await reporting.deleteAllReports(); }); - it('creates a map report using sample geo data', async function () { + it('PNG file matches the baseline image, using sample geo data', async function () { await reporting.initEcommerce(); await PageObjects.common.navigateToApp('dashboard'); @@ -57,7 +57,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await reporting.teardownEcommerce(); }); - it('creates a map report using embeddable example', async function () { + it('PNG file matches the baseline image, using embeddable example', async function () { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.loadSavedDashboard('map embeddable example'); await PageObjects.reporting.openPngReportingPanel(); diff --git a/x-pack/test/functional/apps/visualize/reporting.ts b/x-pack/test/functional/apps/visualize/reporting.ts index 07ce3d9b23128..8f36a50934639 100644 --- a/x-pack/test/functional/apps/visualize/reporting.ts +++ b/x-pack/test/functional/apps/visualize/reporting.ts @@ -6,15 +6,19 @@ */ import expect from '@kbn/expect'; +import path from 'path'; import { FtrProviderContext } from '../../ftr_provider_context'; +const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); + export default function ({ getService, getPageObjects }: FtrProviderContext) { const es = getService('es'); const esArchiver = getService('esArchiver'); const browser = getService('browser'); const log = getService('log'); + const config = getService('config'); const kibanaServer = getService('kibanaServer'); - const ecommerceSOPath = 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce.json'; + const reporting = getService('reporting'); const PageObjects = getPageObjects([ 'reporting', @@ -25,29 +29,45 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'visEditor', ]); - describe('Visualize Reporting Screenshots', () => { + describe('Visualize Reporting Screenshots', function () { + this.tags(['smoke']); before('initialize tests', async () => { log.debug('ReportingPage:initTests'); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/reporting/ecommerce'); - await kibanaServer.importExport.load(ecommerceSOPath); await browser.setWindowSize(1600, 850); - await kibanaServer.uiSettings.replace({ - 'timepicker:timeDefaults': - '{ "from": "2019-04-27T23:56:51.374Z", "to": "2019-08-23T16:18:51.821Z"}', - }); }); - after('clean up archives', async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); - await kibanaServer.importExport.unload(ecommerceSOPath); + after(async () => { await es.deleteByQuery({ index: '.reporting-*', refresh: true, body: { query: { match_all: {} } }, }); - await kibanaServer.uiSettings.unset('timepicker:timeDefaults'); }); describe('Print PDF button', () => { + const ecommerceSOPath = + 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce.json'; + + before('initialize tests', async () => { + log.debug('ReportingPage:initTests'); + await esArchiver.load('x-pack/test/functional/es_archives/reporting/ecommerce'); + await kibanaServer.importExport.load(ecommerceSOPath); + await kibanaServer.uiSettings.replace({ + 'timepicker:timeDefaults': + '{ "from": "2019-04-27T23:56:51.374Z", "to": "2019-08-23T16:18:51.821Z"}', + defaultIndex: '5193f870-d861-11e9-a311-0fa548c5f953', + }); + }); + after('clean up archives', async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); + await kibanaServer.importExport.unload(ecommerceSOPath); + await es.deleteByQuery({ + index: '.reporting-*', + refresh: true, + body: { query: { match_all: {} } }, + }); + await kibanaServer.uiSettings.unset('timepicker:timeDefaults'); + }); + it('is available if new', async () => { await PageObjects.common.navigateToUrl('visualize', 'new', { useActualUrl: true }); await PageObjects.visualize.clickAggBasedVisualizations(); @@ -65,21 +85,69 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.reporting.openPdfReportingPanel(); expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); }); + }); - it('downloaded PDF has OK status', async function () { - // Generating and then comparing reports can take longer than the default 60s timeout - this.timeout(180000); + describe('PNG reports: sample data created in 7.6', () => { + const reportFileName = 'tsvb'; - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); - await PageObjects.reporting.openPdfReportingPanel(); + before(async () => { + await kibanaServer.uiSettings.replace({ + defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + }); + + await esArchiver.load('x-pack/test/functional/es_archives/reporting/ecommerce_76'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json' + ); + + log.debug('navigate to visualize'); + await PageObjects.common.navigateToApp('visualize'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce_76'); + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json' + ); + }); + + it('TSVB Gauge: PNG file matches the baseline image', async function () { + log.debug('load saved visualization'); + await PageObjects.visualize.loadSavedVisualization( + '[K7.6-eCommerce] Sold Products per Day', + { navigateToVisualize: false } + ); + log.debug('set time range'); + await PageObjects.timePicker.setAbsoluteRange( + 'Apr 15, 2022 @ 00:00:00.000', + 'May 22, 2022 @ 00:00:00.000' + ); + + log.debug('open png reporting panel'); + await PageObjects.reporting.openPngReportingPanel(); + log.debug('click generate report button'); await PageObjects.reporting.clickGenerateReportButton(); + log.debug('get the report download URL'); const url = await PageObjects.reporting.getReportURL(60000); - const res = await PageObjects.reporting.getResponse(url); + log.debug('download the report'); + const reportData = await PageObjects.reporting.getRawPdfReportData(url); + const sessionReportPath = await PageObjects.reporting.writeSessionReport( + reportFileName, + 'png', + reportData, + REPORTS_FOLDER + ); + + // check the file + const percentDiff = await reporting.checkIfPngsMatch( + sessionReportPath, + PageObjects.reporting.getBaselineReportPath(reportFileName, 'png', REPORTS_FOLDER), + config.get('screenshots.directory'), + log + ); - expect(res.status).to.equal(200); - expect(res.get('content-type')).to.equal('application/pdf'); + expect(percentDiff).to.be.lessThan(0.09); }); }); }); diff --git a/x-pack/test/functional/apps/visualize/reports/baseline/tsvb.png b/x-pack/test/functional/apps/visualize/reports/baseline/tsvb.png new file mode 100644 index 0000000000000..fbda23e687df5 Binary files /dev/null and b/x-pack/test/functional/apps/visualize/reports/baseline/tsvb.png differ diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_76/data.json.gz b/x-pack/test/functional/es_archives/reporting/ecommerce_76/data.json.gz new file mode 100644 index 0000000000000..80a3aa391811f Binary files /dev/null and b/x-pack/test/functional/es_archives/reporting/ecommerce_76/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_76/mappings.json b/x-pack/test/functional/es_archives/reporting/ecommerce_76/mappings.json new file mode 100644 index 0000000000000..8b2386508f26b --- /dev/null +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_76/mappings.json @@ -0,0 +1,219 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "kibana_sample_data_ecommerce", + "mappings": { + "properties": { + "category": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "currency": { + "type": "keyword" + }, + "customer_birth_date": { + "type": "date" + }, + "customer_first_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_full_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_gender": { + "type": "keyword" + }, + "customer_id": { + "type": "keyword" + }, + "customer_last_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_phone": { + "type": "keyword" + }, + "day_of_week": { + "type": "keyword" + }, + "day_of_week_i": { + "type": "integer" + }, + "email": { + "type": "keyword" + }, + "event": { + "properties": { + "dataset": { + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "type": "keyword" + }, + "continent_name": { + "type": "keyword" + }, + "country_iso_code": { + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword" + } + } + }, + "manufacturer": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "order_date": { + "type": "date" + }, + "order_id": { + "type": "keyword" + }, + "products": { + "properties": { + "_id": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "base_price": { + "type": "half_float" + }, + "base_unit_price": { + "type": "half_float" + }, + "category": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "created_on": { + "type": "date" + }, + "discount_amount": { + "type": "half_float" + }, + "discount_percentage": { + "type": "half_float" + }, + "manufacturer": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "min_price": { + "type": "half_float" + }, + "price": { + "type": "half_float" + }, + "product_id": { + "type": "long" + }, + "product_name": { + "analyzer": "english", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "quantity": { + "type": "integer" + }, + "sku": { + "type": "keyword" + }, + "tax_amount": { + "type": "half_float" + }, + "taxful_price": { + "type": "half_float" + }, + "taxless_price": { + "type": "half_float" + }, + "unit_discount_amount": { + "type": "half_float" + } + } + }, + "sku": { + "type": "keyword" + }, + "taxful_total_price": { + "type": "half_float" + }, + "taxless_total_price": { + "type": "half_float" + }, + "total_quantity": { + "type": "integer" + }, + "total_unique_products": { + "type": "integer" + }, + "type": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json b/x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json new file mode 100644 index 0000000000000..f043f861fd350 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce_76.json @@ -0,0 +1,28 @@ +{"attributes":{"fieldFormatMap":"{\"taxful_total_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.[00]\"}}}","fields":"[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"category\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"category.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"category\"}}},{\"name\":\"currency\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_birth_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_first_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_first_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_first_name\"}}},{\"name\":\"customer_full_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_full_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_full_name\"}}},{\"name\":\"customer_gender\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"customer_last_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"customer_last_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"customer_last_name\"}}},{\"name\":\"customer_phone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"day_of_week\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"day_of_week_i\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoip.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"manufacturer\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"manufacturer.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"manufacturer\"}}},{\"name\":\"order_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"order_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products._id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products._id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products._id\"}}},{\"name\":\"products.base_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.base_unit_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.category\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.category.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.category\"}}},{\"name\":\"products.created_on\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.discount_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.discount_percentage\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.manufacturer\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.manufacturer.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.manufacturer\"}}},{\"name\":\"products.min_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.product_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.product_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"products.product_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"products.product_name\"}}},{\"name\":\"products.quantity\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.sku\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.tax_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.taxful_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.taxless_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"products.unit_discount_amount\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sku\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"taxful_total_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"taxless_total_price\",\"type\":\"number\",\"esTypes\":[\"half_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"total_quantity\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"total_unique_products\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]","timeFieldName":"order_date","title":"kibana_sample_data_ecommerce"},"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","migrationVersion":{"index-pattern":"7.6.0"},"references":[],"type":"index-pattern","updated_at":"2022-05-02T23:12:26.316Z","version":"WzE3LDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Sales by Category","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Sales by Category\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Sum of total_quantity\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Sum of total_quantity\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"metric\",\"params\":{\"field\":\"total_quantity\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"order_date\",\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"category.keyword\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"id":"37cc8650-b882-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzUsMV0="} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Sales by Gender","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Sales by Gender\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"customer_gender\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"id":"ed8436b0-b88b-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzYsMV0="} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"[K7.6-eCommerce] Markdown","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":12,\"openLinksInNewTab\":false,\"markdown\":\"### Sample eCommerce Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html).\"},\"aggs\":[]}"},"id":"09ffee60-b88c-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzcsMV0="} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"[K7.6-eCommerce] Controls","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Controls\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1536977437774\",\"fieldName\":\"manufacturer.keyword\",\"parent\":\"\",\"label\":\"Manufacturer\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1536977465554\",\"fieldName\":\"category.keyword\",\"parent\":\"\",\"label\":\"Category\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"},{\"id\":\"1536977596163\",\"fieldName\":\"total_quantity\",\"parent\":\"\",\"label\":\"Quantity\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1},\"indexPatternRefName\":\"control_2_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":true,\"pinFilters\":false},\"aggs\":[]}"},"id":"1c389590-b88d-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"control_0_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"control_1_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"control_2_index_pattern","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzgsMV0="} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"[K7.6-eCommerce] Promotion Tracking","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Promotion Tracking\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"ea20ae70-b88d-11e8-a451-f37365e9f268\",\"color\":\"rgba(240,138,217,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"ea20ae71-b88d-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":\"0.7\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*trouser*\",\"language\":\"lucene\"},\"label\":\"Revenue Trousers\",\"value_template\":\"${{value}}\"},{\"id\":\"062d77b0-b88e-11e8-a451-f37365e9f268\",\"color\":\"rgba(191,240,129,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"062d77b1-b88e-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":\"0.7\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*watch*\",\"language\":\"lucene\"},\"label\":\"Revenue Watches\",\"value_template\":\"${{value}}\"},{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"rgba(23,233,230,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":\"0.7\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*bag*\",\"language\":\"lucene\"},\"label\":\"Revenue Bags\",\"value_template\":\"${{value}}\"},{\"id\":\"faa2c170-b88d-11e8-a451-f37365e9f268\",\"color\":\"rgba(235,186,180,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"faa2c171-b88d-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":\"0.7\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*cocktail dress*\",\"language\":\"lucene\"},\"label\":\"Revenue Cocktail Dresses\",\"value_template\":\"${{value}}\"}],\"time_field\":\"order_date\",\"index_pattern\":\"kibana_sample_data_ecommerce\",\"interval\":\">=12h\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"legend_position\":\"bottom\",\"annotations\":[{\"fields\":\"taxful_total_price\",\"template\":\"Ring the bell! ${{taxful_total_price}}\",\"index_pattern\":\"kibana_sample_data_ecommerce\",\"query_string\":{\"query\":\"taxful_total_price:>250\",\"language\":\"lucene\"},\"id\":\"c8c30be0-b88f-11e8-a451-f37365e9f268\",\"color\":\"rgba(25,77,51,1)\",\"time_field\":\"order_date\",\"icon\":\"fa-bell\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1}]},\"aggs\":[]}"},"id":"45e07720-b890-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzksMV0="} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Total Revenue","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Total Revenue\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":false},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":36}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"metric\",\"params\":{\"field\":\"taxful_total_price\",\"customLabel\":\"Total Revenue\"}}]}"},"id":"10f1a240-b891-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzEwLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"title":"[K7.6-eCommerce] Sold Products per Day","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Sold Products per Day\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"gauge\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"label\":\"Trxns / day\"}],\"time_field\":\"order_date\",\"index_pattern\":\"kibana_sample_data_ecommerce\",\"interval\":\"1d\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"gauge_color_rules\":[{\"value\":150,\"id\":\"6da070c0-b891-11e8-b645-195edeb9de84\",\"gauge\":\"rgba(104,188,0,1)\",\"operator\":\"gte\"},{\"value\":150,\"id\":\"9b0cdbc0-b891-11e8-b645-195edeb9de84\",\"gauge\":\"rgba(244,78,59,1)\",\"operator\":\"lt\"}],\"gauge_width\":\"15\",\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"filter\":\"\",\"gauge_max\":\"300\"},\"aggs\":[]}"},"id":"b80e6540-b891-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzExLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Average Sales Price","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(165,0,38)\",\"50 - 75\":\"rgb(255,255,190)\",\"75 - 100\":\"rgb(0,104,55)\"}}}","version":1,"visState":"{\"title\":\"[eCommerce] Average Sales Price\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Circle\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":50},{\"from\":50,\"to\":75},{\"from\":75,\"to\":100}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"per order\",\"fontSize\":60,\"labelColor\":true},\"minAngle\":0,\"maxAngle\":6.283185307179586,\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"taxful_total_price\",\"customLabel\":\"average spend\"}}]}"},"id":"4b3ec120-b892-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzEyLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Average Sold Quantity","uiStateJSON":"{\"vis\":{\"defaultColors\":{\"0 - 2\":\"rgb(165,0,38)\",\"2 - 3\":\"rgb(255,255,190)\",\"3 - 4\":\"rgb(0,104,55)\"}}}","version":1,"visState":"{\"title\":\"[eCommerce] Average Sold Quantity\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":false,\"gaugeType\":\"Circle\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":2},{\"from\":2,\"to\":3},{\"from\":3,\"to\":4}],\"invertColors\":true,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"per order\",\"fontSize\":60,\"labelColor\":true},\"minAngle\":0,\"maxAngle\":6.283185307179586,\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"total_quantity\",\"customLabel\":\"average items\"}}]}"},"id":"9ca7aa90-b892-11e8-a6d9-e546fe2bba5f","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzEzLDFd"} + +{"attributes":{"columns":["category","sku","taxful_total_price","total_quantity"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["order_date","desc"]],"title":"[K7.6-eCommerce] Orders","version":1},"id":"3ba638e0-b894-11e8-a6d9-e546fe2bba5f","migrationVersion":{"search":"7.4.0"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search","updated_at":"2022-05-02T23:12:26.316Z","version":"WzE0LDFd"} + +{"attributes":{"bounds":{"coordinates":[[-117.50707,72.64116],[87.35497,-4.16541]],"type":"envelope"},"description":"","layerListJSON":"[{\"id\":\"0hmz5\",\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"visible\":true,\"style\":{},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"7ameq\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"world_countries\",\"tooltipProperties\":[\"name\",\"iso2\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.country_iso_code\",\"origin\":\"join\"},\"color\":\"Green to Red\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"iso2\",\"right\":{\"id\":\"741db9c6-8ebb-4ea9-9885-b6b4ac019d14\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.country_iso_code\",\"indexPatternRefName\":\"layer_1_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"jmtgf\",\"label\":\"United States\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"usa_states\",\"tooltipProperties\":[\"name\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"30a0ec24-49b6-476a-b4ed-6c1636333695\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_2_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"ui5f8\",\"label\":\"France\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"france_departments\",\"tooltipProperties\":[\"label_en\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"id\":\"e325c9da-73fa-4b3b-8b59-364b99370826\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_3_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"y3fjb\",\"label\":\"United Kingdom\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"uk_subdivisions\",\"tooltipProperties\":[\"label_en\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"id\":\"612d805d-8533-43a9-ac0e-cbf51fe63dcd\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_4_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"c54wk\",\"label\":\"Sales\",\"minZoom\":9,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"id\":\"04c983b0-8cfa-4e6a-a64b-52c10b7008fe\",\"type\":\"ES_SEARCH\",\"geoField\":\"geoip.location\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[\"category\",\"customer_gender\",\"manufacturer\",\"order_id\",\"total_quantity\",\"total_unique_products\",\"taxful_total_price\",\"order_date\",\"geoip.region_name\",\"geoip.country_iso_code\"],\"indexPatternRefName\":\"layer_5_source_index_pattern\",\"applyGlobalQuery\":true},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"taxful_total_price\",\"origin\":\"source\"},\"color\":\"Greens\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}}},\"type\":\"VECTOR\"},{\"id\":\"qvhh3\",\"label\":\"Total Sales Revenue\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"aa7f87b8-9dc5-42be-b19e-1a2fa09b6cad\",\"geoField\":\"geoip.location\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"},{\"type\":\"sum\",\"field\":\"taxful_total_price\",\"label\":\"total sales price\"}],\"indexPatternRefName\":\"layer_6_source_index_pattern\",\"applyGlobalQuery\":true},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Greens\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":20,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelText\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"minSize\":12,\"maxSize\":24,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelBorderSize\":{\"options\":{\"size\":\"MEDIUM\"}}}},\"type\":\"VECTOR\"}]","mapStateJSON":"{\"zoom\":2.11,\"center\":{\"lon\":-15.07605,\"lat\":45.88578},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"}}","title":"[K7.6-eCommerce] Orders by Country","uiStateJSON":"{\"isDarkMode\":false}"},"id":"tds99999-1909-11e9-919b-ffe5949a18d2","migrationVersion":{"map":"7.6.0"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_1_join_0_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_2_join_0_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_3_join_0_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_4_join_0_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_5_source_index_pattern","type":"index-pattern"},{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"layer_6_source_index_pattern","type":"index-pattern"}],"type":"map","updated_at":"2022-05-02T23:12:26.316Z","version":"WzIwLDFd"} + +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"[K7.6-eCommerce] Top Selling Products","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"[eCommerce] Top Selling Products\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"products.product_name.keyword\",\"size\":7,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}"},"id":"b72dd430-bb4d-11e8-9c84-77068524bcab","migrationVersion":{"visualization":"7.4.2"},"references":[{"id":"ff959d40-b880-11e8-a6d9-e546fe2bba5f","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-05-02T23:12:26.316Z","version":"WzE2LDFd"} + +{"attributes":{"description":"Analyze mock eCommerce orders and revenue","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"embeddableConfig\":{\"vis\":{\"colors\":{\"Men's Accessories\":\"#82B5D8\",\"Men's Clothing\":\"#F9BA8F\",\"Men's Shoes\":\"#F29191\",\"Women's Accessories\":\"#F4D598\",\"Women's Clothing\":\"#70DBED\",\"Women's Shoes\":\"#B7DBAB\"}}},\"gridData\":{\"x\":12,\"y\":18,\"w\":36,\"h\":10,\"i\":\"1\"},\"panelIndex\":\"1\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_0\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"FEMALE\":\"#6ED0E0\",\"MALE\":\"#447EBC\"},\"legendOpen\":false}},\"gridData\":{\"x\":12,\"y\":7,\"w\":12,\"h\":11,\"i\":\"2\"},\"panelIndex\":\"2\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_1\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":0,\"y\":0,\"w\":18,\"h\":7,\"i\":\"3\"},\"panelIndex\":\"3\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_2\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":18,\"y\":0,\"w\":30,\"h\":7,\"i\":\"4\"},\"panelIndex\":\"4\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_3\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":0,\"y\":28,\"w\":48,\"h\":11,\"i\":\"5\"},\"panelIndex\":\"5\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_4\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":0,\"y\":18,\"w\":12,\"h\":10,\"i\":\"6\"},\"panelIndex\":\"6\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_5\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":0,\"y\":7,\"w\":12,\"h\":11,\"i\":\"7\"},\"panelIndex\":\"7\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_6\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 50\":\"#E24D42\",\"50 - 75\":\"#EAB839\",\"75 - 100\":\"#7EB26D\"},\"defaultColors\":{\"0 - 50\":\"rgb(165,0,38)\",\"50 - 75\":\"rgb(255,255,190)\",\"75 - 100\":\"rgb(0,104,55)\"},\"legendOpen\":false}},\"gridData\":{\"x\":24,\"y\":7,\"w\":12,\"h\":11,\"i\":\"8\"},\"panelIndex\":\"8\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_7\"},{\"embeddableConfig\":{\"vis\":{\"colors\":{\"0 - 2\":\"#E24D42\",\"2 - 3\":\"#F2C96D\",\"3 - 4\":\"#9AC48A\"},\"defaultColors\":{\"0 - 2\":\"rgb(165,0,38)\",\"2 - 3\":\"rgb(255,255,190)\",\"3 - 4\":\"rgb(0,104,55)\"},\"legendOpen\":false}},\"gridData\":{\"x\":36,\"y\":7,\"w\":12,\"h\":11,\"i\":\"9\"},\"panelIndex\":\"9\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_8\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":0,\"y\":54,\"w\":48,\"h\":18,\"i\":\"10\"},\"panelIndex\":\"10\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_9\"},{\"embeddableConfig\":{\"isLayerTOCOpen\":false},\"gridData\":{\"x\":0,\"y\":39,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_10\"},{\"embeddableConfig\":{},\"gridData\":{\"x\":24,\"y\":39,\"w\":24,\"h\":15,\"i\":\"12\"},\"panelIndex\":\"12\",\"version\":\"7.0.0-alpha1\",\"panelRefName\":\"panel_11\"}]","refreshInterval":{"pause":false,"value":900000},"timeFrom":"2022-04-20T00:00:00.000Z","timeRestore":true,"timeTo":"2022-05-22T00:00:00.000Z","title":"[K7.6-eCommerce] Revenue Dashboard","version":1},"id":"722b74f0-b882-11e8-a6d9-e546fe2bba5f","migrationVersion":{"dashboard":"7.3.0"},"references":[{"id":"37cc8650-b882-11e8-a6d9-e546fe2bba5f","name":"panel_0","type":"visualization"},{"id":"ed8436b0-b88b-11e8-a6d9-e546fe2bba5f","name":"panel_1","type":"visualization"},{"id":"09ffee60-b88c-11e8-a6d9-e546fe2bba5f","name":"panel_2","type":"visualization"},{"id":"1c389590-b88d-11e8-a6d9-e546fe2bba5f","name":"panel_3","type":"visualization"},{"id":"45e07720-b890-11e8-a6d9-e546fe2bba5f","name":"panel_4","type":"visualization"},{"id":"10f1a240-b891-11e8-a6d9-e546fe2bba5f","name":"panel_5","type":"visualization"},{"id":"b80e6540-b891-11e8-a6d9-e546fe2bba5f","name":"panel_6","type":"visualization"},{"id":"4b3ec120-b892-11e8-a6d9-e546fe2bba5f","name":"panel_7","type":"visualization"},{"id":"9ca7aa90-b892-11e8-a6d9-e546fe2bba5f","name":"panel_8","type":"visualization"},{"id":"3ba638e0-b894-11e8-a6d9-e546fe2bba5f","name":"panel_9","type":"search"},{"id":"tds99999-1909-11e9-919b-ffe5949a18d2","name":"panel_10","type":"map"},{"id":"b72dd430-bb4d-11e8-9c84-77068524bcab","name":"panel_11","type":"visualization"}],"type":"dashboard","updated_at":"2022-05-02T23:12:26.316Z","version":"WzE4LDFd"} +