diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts index ec8c51af53486..40dce4e9b914e 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts @@ -35,12 +35,6 @@ export interface ElementPosition { }; } -export interface Viewport { - zoom: number; - width: number; - height: number; -} - interface OpenOptions { context?: Context; headers: Headers; @@ -203,7 +197,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; @@ -214,6 +208,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)) { @@ -263,14 +258,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 a0ddfccf592bd..eff3be3d0c439 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 @@ -5,13 +5,13 @@ * 2.0. */ +import type { Logger } from '@kbn/core/server'; +import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; import puppeteer from 'puppeteer'; import * as Rx from 'rxjs'; import { mergeMap, take } from 'rxjs/operators'; -import type { Logger } from '@kbn/core/server'; -import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/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 bfdc74aa43ba6..bec10bf0caec7 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 @@ -5,31 +5,30 @@ * 2.0. */ +import type { Logger } from '@kbn/core/server'; +import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; 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 { catchError, + concatMap, ignoreElements, map, - concatMap, mergeMap, reduce, takeUntil, tap, } from 'rxjs/operators'; -import type { Logger } from '@kbn/core/server'; -import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/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'; @@ -37,12 +36,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; } @@ -63,10 +57,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 @@ -138,6 +138,19 @@ export class HeadlessChromiumDriverFactory { const chromiumArgs = this.getChromiumArgs(); logger.debug(`Chromium launch args set to: ${chromiumArgs}`); + + // 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}` + ); + (async () => { let browser: Browser | undefined; try { @@ -148,13 +161,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 de7278d60840a..814432e9b4374 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/constants.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/constants.ts @@ -8,6 +8,7 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/server'; export const DEFAULT_PAGELOAD_SELECTOR = `.${APP_WRAPPER_CLASS}`; +// FIXME: cleanup: remove this file and use the EventLogger's Actions enum instead export const CONTEXT_GETNUMBEROFITEMS = 'GetNumberOfItems'; export const CONTEXT_INJECTCSS = 'InjectCss'; export const CONTEXT_WAITFORRENDER = 'WaitForRender'; @@ -17,3 +18,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 f3a76ca79d85f..c32135f530bf3 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 @@ -32,11 +32,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 5018701ce2411..9d66f58c308d3 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 @@ -60,9 +60,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 c2342280aea20..950e5551ebc87 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.test.ts @@ -8,6 +8,8 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { createMockBrowserDriver } from '../browsers/mock'; import { ConfigType } from '../config'; +import { Layout } from '../layouts'; +import { createMockLayout } from '../layouts/mock'; import { EventLogger } from './event_logger'; import { getScreenshots } from './get_screenshots'; @@ -31,12 +33,14 @@ describe('getScreenshots', () => { let browser: ReturnType; let eventLogger: EventLogger; let config = {} as ConfigType; + let layout: Layout; beforeEach(async () => { browser = createMockBrowserDriver(); config = { capture: { zoom: 2 } } as ConfigType; eventLogger = new EventLogger(loggingSystemMock.createLogger(), config); browser.evaluate.mockImplementation(({ fn, args }) => (fn as Function)(...args)); + layout = createMockLayout(); }); afterEach(() => { @@ -44,8 +48,8 @@ describe('getScreenshots', () => { }); it('should return screenshots', async () => { - await expect(getScreenshots(browser, eventLogger, elementsPositionAndAttributes)).resolves - .toMatchInlineSnapshot(` + await expect(getScreenshots(browser, eventLogger, elementsPositionAndAttributes, layout)) + .resolves.toMatchInlineSnapshot(` Array [ Object { "data": Object { @@ -90,7 +94,7 @@ describe('getScreenshots', () => { }); it('should forward elements positions', async () => { - await getScreenshots(browser, eventLogger, elementsPositionAndAttributes); + await getScreenshots(browser, eventLogger, elementsPositionAndAttributes, layout); expect(browser.screenshot).toHaveBeenCalledTimes(2); expect(browser.screenshot).toHaveBeenNthCalledWith( @@ -107,7 +111,7 @@ describe('getScreenshots', () => { browser.screenshot.mockResolvedValue(Buffer.from('')); await expect( - getScreenshots(browser, eventLogger, elementsPositionAndAttributes) + getScreenshots(browser, eventLogger, 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 67cfbd111e750..20021f0d90fca 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/get_screenshots.ts @@ -5,15 +5,59 @@ * 2.0. */ -import type { HeadlessChromiumDriver } from '../browsers'; +import { Logger } from '@kbn/logging'; +import { HeadlessChromiumDriver } from '../browsers'; +import { Layout } from '../layouts'; import { Actions, EventLogger } from './event_logger'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; import type { Screenshot } from './types'; +/** + * 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 {EventLogger} eventLogger + * @param {ElementsPositionAndAttribute[]} elements[] - 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, eventLogger: EventLogger, - elementsPositionAndAttributes: ElementsPositionAndAttribute[] + elements: ElementsPositionAndAttribute[], + layout: Layout ): Promise => { const { kbnLogger } = eventLogger; kbnLogger.info(`taking screenshots`); @@ -21,16 +65,20 @@ export const getScreenshots = async ( const screenshots: Screenshot[] = []; try { - for (let i = 0; i < elementsPositionAndAttributes.length; i++) { - const item = elementsPositionAndAttributes[i]; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + const { position, attributes } = element; + + await resizeViewport(browser, position, layout, eventLogger.kbnLogger); + const endScreenshot = eventLogger.logScreenshottingEvent( 'screenshot capture', Actions.GET_SCREENSHOT, 'read', - eventLogger.getPixelsFromElementPosition(item.position) + eventLogger.getPixelsFromElementPosition(position) ); - const data = await browser.screenshot(item.position); + const data = await browser.screenshot(position); if (!data?.byteLength) { throw new Error(`Failure in getScreenshots! Screenshot data is void`); @@ -38,8 +86,8 @@ export const getScreenshots = async ( screenshots.push({ data, - title: item.attributes.title, - description: item.attributes.description, + title: attributes.title, + description: attributes.description, }); endScreenshot({ byte_length: data.byteLength }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts index 33404bb5fadc2..b0b591a16a5d0 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import type { HttpServiceSetup, KibanaRequest, Logger, PackageInfo } from '@kbn/core/server'; import type { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import type { Optional } from '@kbn/utility-types'; @@ -22,16 +23,15 @@ import { tap, toArray, } from 'rxjs/operators'; -import type { CloudSetup } from '@kbn/cloud-plugin/server'; import { + errors, LayoutParams, SCREENSHOTTING_APP_ID, SCREENSHOTTING_EXPRESSION, SCREENSHOTTING_EXPRESSION_INPUT, - errors, } from '../../common'; +import { HeadlessChromiumDriverFactory, PerformanceMetrics } from '../browsers'; import { systemHasInsufficientMemory } from '../cloud'; -import type { HeadlessChromiumDriverFactory, PerformanceMetrics } from '../browsers'; import type { ConfigType } from '../config'; import { durationToNumber } from '../config'; import { @@ -42,8 +42,7 @@ import { toPdf, toPng, } from '../formats'; -import type { Layout } from '../layouts'; -import { createLayout } from '../layouts'; +import { createLayout, Layout } from '../layouts'; import { EventLogger, Transactions } from './event_logger'; import type { ScreenshotObservableOptions, ScreenshotObservableResult } from './observable'; import { ScreenshotObservableHandler, UrlOrUrlWithContext } from './observable'; @@ -109,13 +108,6 @@ export class Screenshots { this.semaphore = new Semaphore(config.poolSize); } - private createLayout(options: CaptureOptions): Layout { - const layout = createLayout(options.layout ?? {}); - this.logger.debug(`Layout: width=${layout.width} height=${layout.height}`); - - return layout; - } - private captureScreenshots( eventLogger: EventLogger, layout: Layout, @@ -128,7 +120,7 @@ export class Screenshots { { browserTimezone, openUrlTimeout: durationToNumber(this.config.capture.timeouts.openUrl), - defaultViewport: { height: layout.height, width: layout.width }, + defaultViewport: { width: layout.width }, }, this.logger ) @@ -233,7 +225,7 @@ export class Screenshots { const eventLogger = new EventLogger(this.logger, this.config); const transactionEnd = eventLogger.startTransaction(Transactions.SCREENSHOTTING); - const layout = this.createLayout(options); + const layout = createLayout(options.layout ?? {}); const captureOptions = this.getCaptureOptions(options); return this.captureScreenshots(eventLogger, layout, captureOptions).pipe( diff --git a/x-pack/plugins/screenshotting/server/screenshots/observable.ts b/x-pack/plugins/screenshotting/server/screenshots/observable.ts index d06014c82ecc7..3766104cd9e12 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/observable.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/observable.ts @@ -9,24 +9,28 @@ import type { Headers } from '@kbn/core/server'; import { defer, forkJoin, Observable, throwError } from 'rxjs'; import { catchError, mergeMap, switchMapTo, timeoutWith } from 'rxjs/operators'; import { errors, LayoutTypes } from '../../common'; -import type { Context, HeadlessChromiumDriver } from '../browsers'; -import { DEFAULT_VIEWPORT, getChromiumDisconnectedError } from '../browsers'; +import { + Context, + DEFAULT_VIEWPORT, + getChromiumDisconnectedError, + HeadlessChromiumDriver, +} from '../browsers'; import { ConfigType, durationToNumber as toNumber } from '../config'; -import type { Layout } from '../layouts'; +import type { PdfScreenshotOptions } from '../formats'; +import { Layout } from '../layouts'; import { Actions, EventLogger } from './event_logger'; import type { ElementsPositionAndAttribute } from './get_element_position_data'; import { getElementPositionAndAttributes } from './get_element_position_data'; import { getNumberOfItems } from './get_number_of_items'; +import { getPdf } from './get_pdf'; import { getRenderErrors } from './get_render_errors'; -import type { Screenshot } from './types'; import { getScreenshots } from './get_screenshots'; -import { getPdf } from './get_pdf'; import { getTimeRange } from './get_time_range'; import { injectCustomCss } from './inject_css'; import { openUrl } from './open_url'; +import type { Screenshot } from './types'; import { waitForRenderComplete } from './wait_for_render'; import { waitForVisualizations } from './wait_for_visualizations'; -import type { PdfScreenshotOptions } from '../formats'; type CaptureTimeouts = ConfigType['capture']['timeouts']; export interface PhaseTimeouts extends CaptureTimeouts { @@ -109,15 +113,6 @@ const getDefaultElementPosition = ( ]; }; -/* - * 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, @@ -173,15 +168,9 @@ export class ScreenshotObservableHandler { const waitTimeout = toNumber(this.config.capture.timeouts.waitForElements); return defer(() => getNumberOfItems(driver, this.eventLogger, 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.eventLogger.kbnLogger); - await waitForVisualizations(driver, this.eventLogger, waitTimeout, itemsCount, this.layout); - }), + mergeMap((itemsCount) => + waitForVisualizations(driver, this.eventLogger, waitTimeout, itemsCount, this.layout) + ), this.waitUntil(waitTimeout, 'wait for elements') ); } @@ -266,7 +255,7 @@ 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 = this.shouldCapturePdf() @@ -276,7 +265,7 @@ export class ScreenshotObservableHandler { this.getTitle(data.timeRange), (this.options as PdfScreenshotOptions).logo ) - : await getScreenshots(this.driver, this.eventLogger, elements); + : await getScreenshots(this.driver, this.eventLogger, 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 bdf8c678eb1d2..6735429765086 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/open_url.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/open_url.ts @@ -7,7 +7,7 @@ import type { Headers } from '@kbn/core/server'; import type { Context, HeadlessChromiumDriver } from '../browsers'; -import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; +import { CONTEXT_DEBUG, DEFAULT_PAGELOAD_SELECTOR } from './constants'; import { Actions, EventLogger } from './event_logger'; export const openUrl = async ( @@ -29,6 +29,27 @@ export const openUrl = async ( try { await browser.open(url, { context, headers, waitForSelector, timeout }, kbnLogger); + + // 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 }, + kbnLogger + ); } catch (err) { kbnLogger.error(err); diff --git a/x-pack/test/examples/reporting_examples/capture_test.ts b/x-pack/test/examples/reporting_examples/capture_test.ts index 16162a15c3121..3557131722b3c 100644 --- a/x-pack/test/examples/reporting_examples/capture_test.ts +++ b/x-pack/test/examples/reporting_examples/capture_test.ts @@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }; describe('Captures', () => { - it('PNG that matches the baseline', async () => { + it('PNG file matches the baseline image', async () => { await PageObjects.common.navigateToApp(appId); await (await testSubjects.find('shareButton')).click(); 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/group1/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/group1/reporting/screenshots.ts index 5a69262801d2e..eda521633f346 100644 --- a/x-pack/test/functional/apps/dashboard/group1/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/group1/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/group3/reports/index.ts b/x-pack/test/functional/apps/maps/group3/reports/index.ts index c892842782aa6..c15e3ce95b4ce 100644 --- a/x-pack/test/functional/apps/maps/group3/reports/index.ts +++ b/x-pack/test/functional/apps/maps/group3/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 45f1e23224b7e..ba9f2a6231f01 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', @@ -27,28 +31,42 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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); + before(async () => { 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(); @@ -66,21 +84,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"} +