diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index cb2ae4cc0759d..11feea642deb0 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -62,6 +62,7 @@ export const LAYOUT_TYPES = { export const DEFAULT_VIEWPORT = { width: 1950, height: 1200, + deviceScaleFactor: 1, }; // Export Type Definitions diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts index cee6ee8caa0f8..1f178ea7b2ad3 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts @@ -175,6 +175,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)) { @@ -255,7 +256,7 @@ export class HeadlessChromiumDriver { 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/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 264e673d2bf74..78325ef903710 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -17,6 +17,7 @@ import { InnerSubscriber } from 'rxjs/internal/InnerSubscriber'; import { ignoreElements, map, mergeMap, tap } from 'rxjs/operators'; import { getChromiumDisconnectedError } from '../'; import { ReportingCore } from '../../..'; +import { DEFAULT_VIEWPORT } from '../../../../common/constants'; import { durationToNumber } from '../../../../common/schema_utils'; import { CaptureConfig } from '../../../../server/types'; import { LevelLogger } from '../../../lib'; @@ -25,6 +26,11 @@ import { HeadlessChromiumDriver } from '../driver'; import { args } from './args'; import { getMetrics, Metrics } from './metrics'; +interface CreatePageOptions { + browserTimezone?: string; + defaultViewport: { width?: number }; +} + type BrowserConfig = CaptureConfig['browser']['chromium']; export class HeadlessChromiumDriverFactory { @@ -61,7 +67,7 @@ export class HeadlessChromiumDriverFactory { * Return an observable to objects which will drive screenshot capture for a page */ createPage( - { browserTimezone }: { browserTimezone?: string }, + { browserTimezone, defaultViewport }: CreatePageOptions, pLogger: LevelLogger ): Rx.Observable<{ driver: HeadlessChromiumDriver; exit$: Rx.Observable }> { // FIXME: 'create' is deprecated @@ -69,6 +75,17 @@ export class HeadlessChromiumDriverFactory { const logger = pLogger.clone(['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}`); @@ -85,6 +102,7 @@ export class HeadlessChromiumDriverFactory { ignoreHTTPSErrors: true, handleSIGHUP: false, args: chromiumArgs, + defaultViewport: viewport, env: { TZ: browserTimezone, }, diff --git a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts index c5e70a6c93eff..29ddd9f3f8ef4 100644 --- a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts +++ b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts @@ -39,6 +39,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { const apmScreenshots = apmTrans?.startSpan('screenshots-pipeline', 'setup'); let apmBuffer: typeof apm.currentSpan; + logger.debug(`Layout: id=${layout.id} width=${layout.width} height=${layout.height}`); const screenshots$ = getScreenshots$(captureConfig, browserDriverFactory, { logger, urlsOrUrlLocatorTuples: [urlOrUrlLocatorTuple], diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts index b44e2ca4441eb..f497816cbf51e 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts @@ -48,7 +48,6 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) { tracker.startLayout(); const layout = createLayout(captureConfig, layoutParams); - logger.debug(`Layout: width=${layout.width} height=${layout.height}`); tracker.endLayout(); tracker.startScreenshots(); @@ -60,6 +59,7 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) { getFullRedirectAppUrl(reporting.getConfig(), job.spaceId, job.forceNow) ); + logger.debug(`Layout: id=${layout.id} width=${layout.width} height=${layout.height}`); const screenshots$ = getScreenshots$(captureConfig, browserDriverFactory, { logger, urlsOrUrlLocatorTuples: zip(urls, locatorParams) as UrlOrUrlLocatorTuple[], diff --git a/x-pack/plugins/reporting/server/lib/layouts/canvas_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/canvas_layout.ts index ec95b0f75997d..16759f06707f3 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/canvas_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/canvas_layout.ts @@ -38,10 +38,14 @@ export class CanvasLayout extends Layout implements LayoutInstance { constructor(size: Size) { super(LayoutTypes.CANVAS); - this.height = size.height; - this.width = size.width; - this.scaledHeight = size.height * ZOOM; - this.scaledWidth = size.width * ZOOM; + + const height = Math.round(size.height); + this.height = height; + this.scaledHeight = height * ZOOM; + + const width = Math.round(size.width); + this.width = width; + this.scaledWidth = width * ZOOM; } public getPdfPageOrientation() { diff --git a/x-pack/plugins/reporting/server/lib/layouts/create_layout.test.ts b/x-pack/plugins/reporting/server/lib/layouts/create_layout.test.ts index aebd20451b834..40bb1cbd34074 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/create_layout.test.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/create_layout.test.ts @@ -72,6 +72,7 @@ describe('Create Layout', () => { }, "useReportingBranding": true, "viewport": Object { + "deviceScaleFactor": 1, "height": 1200, "width": 1950, }, diff --git a/x-pack/plugins/reporting/server/lib/layouts/layout.ts b/x-pack/plugins/reporting/server/lib/layouts/layout.ts index d68e7690d79f1..ef15454cd19fc 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/layout.ts @@ -34,9 +34,16 @@ export abstract class Layout { 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/reporting/server/lib/layouts/preserve_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts index 7f6bc9e5d9505..9fab859aa8d32 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts @@ -24,10 +24,14 @@ export class PreserveLayout extends Layout implements LayoutInstance { constructor(size: Size, selectors?: Partial) { super(LAYOUT_TYPES.PRESERVE_LAYOUT); - this.height = size.height; - this.width = size.width; - this.scaledHeight = size.height * ZOOM; - this.scaledWidth = size.width * ZOOM; + + const height = Math.round(size.height); + this.height = height; + this.scaledHeight = height * ZOOM; + + const width = Math.round(size.width); + this.width = width; + this.scaledWidth = width * ZOOM; this.selectors = { ...getDefaultLayoutSelectors(), diff --git a/x-pack/plugins/reporting/server/lib/screenshots/constants.ts b/x-pack/plugins/reporting/server/lib/screenshots/constants.ts index c62b910630874..624b4a9e09143 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/constants.ts +++ b/x-pack/plugins/reporting/server/lib/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/reporting/server/lib/screenshots/get_element_position_data.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.test.ts index 389ae4f49f3b6..7b08e380b5a79 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.test.ts @@ -58,8 +58,8 @@ describe('getElementPositionAndAttributes', () => { 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/reporting/server/lib/screenshots/get_element_position_data.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.ts index 39163843c732f..293eb692f447c 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_element_position_data.ts @@ -32,9 +32,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/reporting/server/lib/screenshots/get_screenshots.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.test.ts index edd346c9b8928..3c240bdf65dd7 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.test.ts @@ -8,10 +8,13 @@ import { HeadlessChromiumDriver } from '../../browsers'; import { createMockBrowserDriverFactory, + createMockConfig, createMockConfigSchema, + createMockLayoutInstance, createMockLevelLogger, createMockReportingCore, } from '../../test_helpers'; +import { LayoutInstance } from '../layouts'; import { getScreenshots } from './get_screenshots'; describe('getScreenshots', () => { @@ -34,6 +37,7 @@ describe('getScreenshots', () => { let logger: ReturnType; let browser: jest.Mocked; + let layout: LayoutInstance; beforeEach(async () => { const core = await createMockReportingCore(createMockConfigSchema()); @@ -56,6 +60,8 @@ describe('getScreenshots', () => { return jest.fn(); }, }); + const config = createMockConfig(createMockConfigSchema()); + layout = createMockLayoutInstance(config.get('capture')); }); afterEach(() => { @@ -63,7 +69,7 @@ describe('getScreenshots', () => { }); it('should return screenshots', async () => { - await expect(getScreenshots(browser, elementsPositionAndAttributes, logger)).resolves + await expect(getScreenshots(browser, layout, elementsPositionAndAttributes, logger)).resolves .toMatchInlineSnapshot(` Array [ Object { @@ -109,7 +115,7 @@ describe('getScreenshots', () => { }); it('should forward elements positions', async () => { - await getScreenshots(browser, elementsPositionAndAttributes, logger); + await getScreenshots(browser, layout, elementsPositionAndAttributes, logger); expect(browser.screenshot).toHaveBeenCalledTimes(2); expect(browser.screenshot).toHaveBeenNthCalledWith( @@ -126,7 +132,7 @@ describe('getScreenshots', () => { browser.screenshot.mockResolvedValue(Buffer.from('')); await expect( - getScreenshots(browser, elementsPositionAndAttributes, logger) + getScreenshots(browser, layout, elementsPositionAndAttributes, logger) ).rejects.toBeInstanceOf(Error); }); }); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.ts index 9b5f234b78363..02b922a722408 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.ts @@ -5,21 +5,59 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { LevelLogger, startTrace } from '../'; import { HeadlessChromiumDriver } from '../../browsers'; import { ElementsPositionAndAttribute, Screenshot } from './'; +import { LayoutInstance } from '../layouts'; +/** + * 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: LayoutInstance, + logger: LevelLogger +) => { + 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, + layout: LayoutInstance, elementsPositionAndAttributes: ElementsPositionAndAttribute[], logger: LevelLogger ): Promise => { - logger.info( - i18n.translate('xpack.reporting.screencapture.takingScreenshots', { - defaultMessage: `taking screenshots`, - }) - ); + logger.info(`taking screenshots`); const screenshots: Screenshot[] = []; @@ -27,6 +65,8 @@ export const getScreenshots = async ( const endTrace = startTrace('get_screenshots', 'read'); const item = elementsPositionAndAttributes[i]; + await resizeViewport(browser, item.position, layout, logger); + const data = await browser.screenshot(item.position); if (!data?.byteLength) { @@ -42,14 +82,7 @@ export const getScreenshots = async ( endTrace(); } - logger.info( - i18n.translate('xpack.reporting.screencapture.screenshotsTaken', { - defaultMessage: `screenshots taken: {numScreenhots}`, - values: { - numScreenhots: screenshots.length, - }, - }) - ); + logger.info(`screenshots taken: ${screenshots.length}`); return screenshots; }; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts index 3071ecb54dc26..757570a2c76e6 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts @@ -450,7 +450,7 @@ describe('Screenshot Observable Pipeline', () => { "height": 1200, "left": 0, "top": 0, - "width": 1800, + "width": 1950, }, "scroll": Object { "x": 0, diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts index 8ba2a125a5504..2788b8c0befff 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts @@ -15,7 +15,6 @@ import { CaptureConfig } from '../../types'; import { ElementPosition, ElementsPositionAndAttribute, - PageSetupResults, ScreenshotObservableOpts, ScreenshotResults, } from './'; @@ -42,44 +41,54 @@ const getTimeouts = (captureConfig: CaptureConfig) => ({ loadDelay: durationToNumber(captureConfig.loadDelay), }); +const DEFAULT_SETUP_RESULT = { + elementsPositionAndAttributes: null, + timeRange: null, +}; + export function getScreenshots$( captureConfig: CaptureConfig, browserDriverFactory: HeadlessChromiumDriverFactory, - opts: ScreenshotObservableOpts + options: ScreenshotObservableOpts ): Rx.Observable { const apmTrans = apm.startTransaction('screenshot-pipeline', REPORTING_TRANSACTION_TYPE); + const { layout } = options; const apmCreatePage = apmTrans?.startSpan('create-page', 'wait'); - const { browserTimezone, logger } = opts; + const { browserTimezone, logger } = options; - return browserDriverFactory.createPage({ browserTimezone }, logger).pipe( - mergeMap(({ driver, exit$ }) => { - apmCreatePage?.end(); - exit$.subscribe({ error: () => apmTrans?.end() }); + return browserDriverFactory + .createPage( + { + browserTimezone, + // not provided in 7.17: openUrlTimeout + defaultViewport: { width: layout.width }, + }, + logger + ) + .pipe( + mergeMap(({ driver, exit$ }) => { + apmCreatePage?.end(); + exit$.subscribe({ error: () => apmTrans?.end() }); - const screen = new ScreenshotObservableHandler(driver, opts, getTimeouts(captureConfig)); + const screen = new ScreenshotObservableHandler(driver, options, getTimeouts(captureConfig)); - return Rx.from(opts.urlsOrUrlLocatorTuples).pipe( - concatMap((urlOrUrlLocatorTuple, index) => - screen.setupPage(index, urlOrUrlLocatorTuple, apmTrans).pipe( - catchError((err) => { - screen.checkPageIsOpen(); // this fails the job if the browser has closed + return Rx.from(options.urlsOrUrlLocatorTuples).pipe( + concatMap((urlOrUrlLocatorTuple, index) => + screen.setupPage(index, urlOrUrlLocatorTuple, apmTrans).pipe( + catchError((err) => { + screen.checkPageIsOpen(); // this fails the job if the browser has closed - logger.error(err); - return Rx.of({ ...defaultSetupResult, error: err }); // allow failover screenshot capture - }), - takeUntil(exit$), - screen.getScreenshots() - ) - ), - take(opts.urlsOrUrlLocatorTuples.length), - toArray() - ); - }), - first() - ); + logger.error(err); + return Rx.of({ ...DEFAULT_SETUP_RESULT, error: err }); // allow failover screenshot capture + }), + takeUntil(exit$), + screen.getScreenshots() + ) + ), + take(options.urlsOrUrlLocatorTuples.length), + toArray() + ); + }), + first() + ); } - -const defaultSetupResult: PageSetupResults = { - elementsPositionAndAttributes: null, - timeRange: null, -}; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts index cb0a513992722..5047831564e5d 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts @@ -78,7 +78,10 @@ describe('ScreenshotObservableHandler', () => { beforeEach(async () => { const reporting = await createMockReportingCore(createMockConfigSchema()); const driverFactory = await createMockBrowserDriverFactory(reporting, logger); - ({ driver } = await driverFactory.createPage({}, logger).pipe(first()).toPromise()); + ({ driver } = await driverFactory + .createPage({ defaultViewport: { width: 2020 } }, logger) + .pipe(first()) + .toPromise()); driver.isPageOpen = jest.fn().mockImplementation(() => true); }); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts index c241a529818fa..5dec42c33f4cb 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts @@ -8,6 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, mergeMap, switchMapTo, timeoutWith } from 'rxjs/operators'; +import { DEFAULT_VIEWPORT } from '../../../common/constants'; import { numberToDuration } from '../../../common/schema_utils'; import { UrlOrUrlLocatorTuple } from '../../../common/types'; import { HeadlessChromiumDriver } from '../../browsers'; @@ -29,6 +30,20 @@ import { openUrl } from './open_url'; import { waitForRenderComplete } from './wait_for_render'; import { waitForVisualizations } from './wait_for_visualizations'; +const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => { + const height = dimensions?.height || DEFAULT_VIEWPORT.height; + const width = dimensions?.width || DEFAULT_VIEWPORT.width; + + return [ + { + position: { + boundingClientRect: { top: 0, left: 0, height, width }, + scroll: { x: 0, y: 0 }, + }, + attributes: {}, + }, + ]; +}; export class ScreenshotObservableHandler { private conditionalHeaders: ScreenshotObservableOpts['conditionalHeaders']; private layout: ScreenshotObservableOpts['layout']; @@ -87,15 +102,9 @@ export class ScreenshotObservableHandler { const waitTimeout = this.timeouts.waitForElements.timeoutValue; return Rx.defer(() => getNumberOfItems(waitTimeout, driver, this.layout, this.logger)).pipe( - mergeMap((itemsCount) => { - // set the viewport to the dimentions from the job, to allow elements to flow into the expected layout - const viewport = this.layout.getViewport(itemsCount) || getDefaultViewPort(); - - return Rx.forkJoin([ - driver.setViewport(viewport, this.logger), - waitForVisualizations(waitTimeout, driver, itemsCount, this.layout, this.logger), - ]); - }), + mergeMap((itemsCount) => + waitForVisualizations(waitTimeout, driver, itemsCount, this.layout, this.logger) + ), this.waitUntil(this.timeouts.waitForElements) ); } @@ -147,8 +156,8 @@ export class ScreenshotObservableHandler { const elements = data.elementsPositionAndAttributes ?? - getDefaultElementPosition(this.layout.getViewport(1)); - const screenshots = await getScreenshots(this.driver, elements, this.logger); + getDefaultElementPosition(this.layout.getViewport()); + const screenshots = await getScreenshots(this.driver, this.layout, elements, this.logger); const { timeRange, error: setupError } = data; return { @@ -167,31 +176,3 @@ export class ScreenshotObservableHandler { } } } - -const DEFAULT_SCREENSHOT_CLIP_HEIGHT = 1200; -const DEFAULT_SCREENSHOT_CLIP_WIDTH = 1800; - -const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => { - const height = dimensions?.height || DEFAULT_SCREENSHOT_CLIP_HEIGHT; - const width = dimensions?.width || DEFAULT_SCREENSHOT_CLIP_WIDTH; - - return [ - { - position: { - boundingClientRect: { top: 0, left: 0, height, width }, - scroll: { x: 0, y: 0 }, - }, - attributes: {}, - }, - ]; -}; - -/* - * If Kibana is showing a non-HTML error message, the viewport might not be - * provided by the browser. - */ -const getDefaultViewPort = () => ({ - height: DEFAULT_SCREENSHOT_CLIP_HEIGHT, - width: DEFAULT_SCREENSHOT_CLIP_WIDTH, - zoom: 1, -}); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts index b26037aa917b8..23e3017c24aec 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts @@ -11,7 +11,7 @@ import { LocatorParams, UrlOrUrlLocatorTuple } from '../../../common/types'; import { HeadlessChromiumDriver } from '../../browsers'; import { ConditionalHeaders } from '../../export_types/common'; import { Layout } from '../layouts'; -import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; +import { CONTEXT_DEBUG, DEFAULT_PAGELOAD_SELECTOR } from './constants'; export const openUrl = async ( timeout: number, @@ -43,6 +43,27 @@ export const openUrl = async ( { conditionalHeaders, waitForSelector, timeout, locator, layout }, 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( diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts index d42fb73b447a5..54ddfa8810822 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_browserdriverfactory.ts @@ -60,6 +60,9 @@ mockBrowserEvaluate.mockImplementation(() => { const lastCallIndex = mockBrowserEvaluate.mock.calls.length - 1; const { context: mockCall } = mockBrowserEvaluate.mock.calls[lastCallIndex][1]; + if (mockCall === contexts.CONTEXT_DEBUG) { + return Promise.resolve(); + } if (mockCall === contexts.CONTEXT_SKIPTELEMETRY) { return Promise.resolve(); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 686856f8bf57f..5053b78d5002d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20721,8 +20721,6 @@ "xpack.reporting.screencapture.readVisualizationsError": "ビジュアライゼーションパネル情報のページを読み取ろうとしたときにエラーが発生しました:{error}", "xpack.reporting.screencapture.renderErrorsFound": "{count}件のエラーメッセージが見つかりました。詳細については、レポートオブジェクトを参照してください。", "xpack.reporting.screencapture.renderIsComplete": "レンダリングが完了しました", - "xpack.reporting.screencapture.screenshotsTaken": "撮影したスクリーンショット:{numScreenhots}", - "xpack.reporting.screencapture.takingScreenshots": "スクリーンショットの撮影中", "xpack.reporting.screencapture.waitingForRenderComplete": "レンダリングの完了を待っています", "xpack.reporting.screencapture.waitingForRenderedElements": "レンダリングされた {itemsCount} 個の要素が DOM に入るのを待っています", "xpack.reporting.screenCapturePanelContent.canvasLayoutHelpText": "枠線とフッターロゴを削除", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4c03a1c94df57..d3797ef12e954 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21021,8 +21021,6 @@ "xpack.reporting.screencapture.readVisualizationsError": "尝试读取页面以获取可视化面板信息时发生错误:{error}", "xpack.reporting.screencapture.renderErrorsFound": "找到 {count} 条错误消息。请参阅报告对象了解更多信息。", "xpack.reporting.screencapture.renderIsComplete": "渲染已完成", - "xpack.reporting.screencapture.screenshotsTaken": "已捕获的屏幕截图:{numScreenhots}", - "xpack.reporting.screencapture.takingScreenshots": "正在捕获屏幕截图", "xpack.reporting.screencapture.waitingForRenderComplete": "正在等候渲染完成", "xpack.reporting.screencapture.waitingForRenderedElements": "正在等候 {itemsCount} 个已渲染元素进入 DOM", "xpack.reporting.screenCapturePanelContent.canvasLayoutHelpText": "删除边框和页脚徽标", diff --git a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts index a2523c6d44244..579cf4879a835 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts @@ -29,14 +29,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); 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', { @@ -44,7 +51,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { cluster: [], indices: [ { - names: ['ecommerce'], + names: ['ecommerce', 'kibana_sample_data_ecommerce'], privileges: ['read'], field_security: { grant: ['*'], except: [] }, }, @@ -66,8 +73,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, @@ -93,6 +99,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. @@ -112,6 +125,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(); @@ -128,6 +148,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('PNG Layout', () => { + before(async () => { + await loadEcommerce(); + }); + after(async () => { + await unloadEcommerce(); + }); + const writeSessionReport = async (name: string, rawPdf: Buffer, reportExt: string) => { const sessionDirectory = path.resolve(REPORTS_FOLDER, 'session'); await mkdirAsync(sessionDirectory, { recursive: true }); @@ -166,7 +193,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'); @@ -192,6 +219,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'); diff --git a/x-pack/test/functional/apps/maps/index.js b/x-pack/test/functional/apps/maps/index.js index 353c25ecb44a0..729af2702f289 100644 --- a/x-pack/test/functional/apps/maps/index.js +++ b/x-pack/test/functional/apps/maps/index.js @@ -48,6 +48,11 @@ export default function ({ loadTestFile, getService }) { loadTestFile(require.resolve('./embeddable')); }); + describe('', function () { + this.tags('ciGroup2'); // same group used in x-pack/test/reporting_functional + loadTestFile(require.resolve('./reports')); + }); + describe('', function () { this.tags('ciGroup10'); loadTestFile(require.resolve('./es_pew_pew_source')); diff --git a/x-pack/test/functional/apps/maps/reports/baseline/example_map_report.png b/x-pack/test/functional/apps/maps/reports/baseline/example_map_report.png new file mode 100644 index 0000000000000..11185fd271579 Binary files /dev/null and b/x-pack/test/functional/apps/maps/reports/baseline/example_map_report.png differ diff --git a/x-pack/test/functional/apps/maps/reports/index.ts b/x-pack/test/functional/apps/maps/reports/index.ts new file mode 100644 index 0000000000000..863a01c03228c --- /dev/null +++ b/x-pack/test/functional/apps/maps/reports/index.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const REPORTS_FOLDER = __dirname; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); + const config = getService('config'); + const log = getService('log'); + const reporting = getService('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); + const reportData = await PageObjects.reporting.getRawPdfReportData(url); + + const sessionReportPath = await PageObjects.reporting.writeSessionReport( + fileName, + 'png', + reportData, + REPORTS_FOLDER + ); + log.debug(`session report path: ${sessionReportPath}`); + + expect(sessionReportPath).not.to.be(null); + return await reporting.checkIfPngsMatch( + sessionReportPath, + PageObjects.reporting.getBaselineReportPath(fileName, 'png', REPORTS_FOLDER), + config.get('screenshots.directory'), + log + ); + }; + + after(async () => { + await reporting.deleteAllReports(); + }); + + 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(); + await PageObjects.reporting.clickGenerateReportButton(); + + const percentDiff = await measurePngDifference('example_map_report'); + expect(percentDiff).to.be.lessThan(0.09); + }); + }); +} 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..083fccc766599 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"} + diff --git a/x-pack/test/functional/page_objects/reporting_page.ts b/x-pack/test/functional/page_objects/reporting_page.ts index 039f4ff0fbc57..234e19bf90af0 100644 --- a/x-pack/test/functional/page_objects/reporting_page.ts +++ b/x-pack/test/functional/page_objects/reporting_page.ts @@ -6,11 +6,16 @@ */ import expect from '@kbn/expect'; -import { format as formatUrl } from 'url'; +import fs from 'fs'; +import path from 'path'; import type SuperTest from 'supertest'; - -import { FtrService } from '../ftr_provider_context'; +import { format as formatUrl } from 'url'; +import { promisify } from 'util'; import { REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '../../../plugins/reporting/common/constants'; +import { FtrService } from '../ftr_provider_context'; + +const writeFileAsync = promisify(fs.writeFile); +const mkdirAsync = promisify(fs.mkdir); export class ReportingPageObject extends FtrService { private readonly browser = this.ctx.getService('browser'); @@ -186,4 +191,20 @@ export class ReportingPageObject extends FtrService { }) ); } + + async writeSessionReport(name: string, reportExt: string, rawPdf: Buffer, folder: string) { + const sessionDirectory = path.resolve(folder, 'session'); + await mkdirAsync(sessionDirectory, { recursive: true }); + const sessionReportPath = path.resolve(sessionDirectory, `${name}.${reportExt}`); + await writeFileAsync(sessionReportPath, rawPdf); + this.log.debug(`sessionReportPath (${sessionReportPath})`); + return sessionReportPath; + } + + getBaselineReportPath(fileName: string, reportExt: string, folder: string) { + const baselineFolder = path.resolve(folder, 'baseline'); + const fullPath = path.resolve(baselineFolder, `${fileName}.${reportExt}`); + this.log.debug(`getBaselineReportPath (${fullPath})`); + return fullPath; + } } diff --git a/x-pack/test/reporting_functional/services/scenarios.ts b/x-pack/test/reporting_functional/services/scenarios.ts index a1387127ffc0a..b17c753961ac4 100644 --- a/x-pack/test/reporting_functional/services/scenarios.ts +++ b/x-pack/test/reporting_functional/services/scenarios.ts @@ -6,8 +6,9 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { checkIfPngsMatch } from '../../functional/apps/dashboard/reporting/lib/compare_pngs'; import { createScenarios as createAPIScenarios } from '../../reporting_api_integration/services/scenarios'; +import { FtrProviderContext } from '../ftr_provider_context'; export function createScenarios( context: Pick @@ -161,5 +162,6 @@ export function createScenarios( tryReportsNotAvailable, loginDataAnalyst, loginReportingUser, + checkIfPngsMatch, }; }