"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createVisualReport = void 0;

var _puppeteerCore = _interopRequireDefault(require("puppeteer-core"));

var _dompurify = _interopRequireDefault(require("dompurify"));

var _jsdom = require("jsdom");

var _constants = require("../constants");

var _helpers = require("../helpers");

var _fs = _interopRequireDefault(require("fs"));

var _lodash = _interopRequireDefault(require("lodash"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */
const createVisualReport = async (reportParams, queryUrl, logger, extraHeaders, timezone, validRequestProtocol = /^(data:image)/) => {
  const {
    core_params,
    report_name: reportName,
    report_source: reportSource
  } = reportParams;
  const coreParams = core_params;
  const {
    header,
    footer,
    window_height: windowHeight,
    window_width: windowWidth,
    report_format: reportFormat
  } = coreParams;
  const window = new _jsdom.JSDOM('').window;
  const DOMPurify = (0, _dompurify.default)(window);
  let keywordFilteredHeader = header ? _constants.converter.makeHtml(header) : _constants.DEFAULT_REPORT_HEADER;
  let keywordFilteredFooter = footer ? _constants.converter.makeHtml(footer) : '';
  keywordFilteredHeader = DOMPurify.sanitize(keywordFilteredHeader);
  keywordFilteredFooter = DOMPurify.sanitize(keywordFilteredFooter); // filter blocked keywords in header and footer

  if (keywordFilteredHeader !== '') {
    keywordFilteredHeader = (0, _constants.replaceBlockedKeywords)(keywordFilteredHeader);
  }

  if (keywordFilteredFooter !== '') {
    keywordFilteredFooter = (0, _constants.replaceBlockedKeywords)(keywordFilteredFooter);
  } // set up puppeteer


  const browser = await _puppeteerCore.default.launch({
    headless: true,

    /**
     * TODO: temp fix to disable sandbox when launching chromium on Linux instance
     * https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox
     */
    args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', '--no-zygote', '--single-process', '--font-render-hinting=none', '--js-flags="--jitless --no-opt"', '--disable-features=V8OptimizeJavascript'],
    executablePath: _constants.CHROMIUM_PATH,
    ignoreHTTPSErrors: true,
    env: {
      TZ: timezone || 'UTC'
    }
  });
  const page = await browser.newPage();
  await page.setRequestInterception(true);
  let localStorageAvailable = true;
  page.on('request', req => {
    // disallow non-allowlisted connections. urls with valid protocols do not need ALLOWED_HOSTS check
    if (!validRequestProtocol.test(req.url()) && !_constants.ALLOWED_HOSTS.test(new URL(req.url()).hostname)) {
      if (req.isNavigationRequest() && req.redirectChain().length > 0) {
        localStorageAvailable = false;
        logger.error('Reporting does not allow redirections to outside of localhost, aborting. URL received: ' + req.url());
      } else {
        logger.warn('Disabled connection to non-allowlist domains: ' + req.url());
      }

      req.abort();
    } else {
      req.continue();
    }
  });
  page.setDefaultNavigationTimeout(0);
  page.setDefaultTimeout(100000); // use 100s timeout instead of default 30s
  // Set extra headers that are needed

  if (!_lodash.default.isEmpty(extraHeaders)) {
    await page.setExtraHTTPHeaders(extraHeaders);
  }

  logger.info(`original queryUrl ${queryUrl}`);
  await page.goto(queryUrl, {
    waitUntil: 'networkidle0'
  }); // should add to local storage after page.goto, then access the page again - browser must have an url to register local storage item on it

  try {
    await page.evaluate(
    /* istanbul ignore next */
    key => {
      try {
        if (localStorageAvailable && typeof localStorage !== 'undefined' && localStorage !== null) {
          localStorage.setItem(key, 'false');
        }
      } catch (err) {}
    }, _constants.SECURITY_CONSTANTS.TENANT_LOCAL_STORAGE_KEY);
  } catch (err) {
    logger.error(err);
  }

  await page.goto(queryUrl, {
    waitUntil: 'networkidle0'
  });
  logger.info(`page url ${page.url()}`);
  await page.setViewport({
    width: windowWidth,
    height: windowHeight
  });
  let buffer; // remove unwanted elements

  await page.evaluate(
  /* istanbul ignore next */
  (reportSource, REPORT_TYPE) => {
    // remove buttons
    document.querySelectorAll("[class^='euiButton']").forEach(e => e.remove()); // remove top navBar

    document.querySelectorAll("[class^='euiHeader']").forEach(e => e.remove()); // remove visualization editor

    if (reportSource === REPORT_TYPE.visualization) {
      var _document$querySelect, _document$querySelect2;

      (_document$querySelect = document.querySelector('[data-test-subj="splitPanelResizer"]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.remove();
      (_document$querySelect2 = document.querySelector('.visEditor__collapsibleSidebar')) === null || _document$querySelect2 === void 0 ? void 0 : _document$querySelect2.remove();
    }

    document.body.style.paddingTop = '0px';
  }, reportSource, _constants.REPORT_TYPE); // force wait for any resize to load after the above DOM modification

  await page.waitFor(1000); // crop content

  switch (reportSource) {
    case _constants.REPORT_TYPE.dashboard:
      await page.waitForSelector(_constants.SELECTOR.dashboard, {
        visible: true
      });
      break;

    case _constants.REPORT_TYPE.visualization:
      await page.waitForSelector(_constants.SELECTOR.visualization, {
        visible: true
      });
      break;

    case _constants.REPORT_TYPE.notebook:
      await page.waitForSelector(_constants.SELECTOR.notebook, {
        visible: true
      });
      break;

    default:
      throw Error(`report source can only be one of [Dashboard, Visualization]`);
  } // wait for dynamic page content to render


  await waitForDynamicContent(page);
  await addReportHeader(page, keywordFilteredHeader);
  await addReportFooter(page, keywordFilteredFooter);
  await addReportStyle(page); // this causes UT to fail in github CI but works locally

  try {
    const numDisallowedTags = await page.evaluate(() => document.getElementsByTagName('iframe').length + document.getElementsByTagName('embed').length + document.getElementsByTagName('object').length);

    if (numDisallowedTags > 0) {
      throw Error('Reporting does not support "iframe", "embed", or "object" tags, aborting');
    }
  } catch (error) {
    logger.error(error);
  } // create pdf or png accordingly


  if (reportFormat === _constants.FORMAT.pdf) {
    const scrollHeight = await page.evaluate(
    /* istanbul ignore next */
    () => document.documentElement.scrollHeight);
    buffer = await page.pdf({
      margin: undefined,
      width: windowWidth,
      height: scrollHeight + 'px',
      printBackground: true,
      pageRanges: '1'
    });
  } else if (reportFormat === _constants.FORMAT.png) {
    buffer = await page.screenshot({
      fullPage: true
    });
  }

  const curTime = new Date();
  const timeCreated = curTime.valueOf();
  const fileName = `${(0, _helpers.getFileName)(reportName, curTime)}.${reportFormat}`;
  await browser.close();
  return {
    timeCreated,
    dataUrl: buffer.toString('base64'),
    fileName
  };
};

exports.createVisualReport = createVisualReport;

const addReportStyle = async page => {
  const css = _fs.default.readFileSync(`${__dirname}/style.css`).toString();

  await page.evaluate(
  /* istanbul ignore next */
  style => {
    const styleElement = document.createElement('style');
    styleElement.innerHTML = style;
    document.getElementsByTagName('head')[0].appendChild(styleElement);
  }, css);
};

const addReportHeader = async (page, header) => {
  const headerHtml = _fs.default.readFileSync(`${__dirname}/header_template.html`).toString().replace('<!--CONTENT-->', header);

  await page.evaluate(
  /* istanbul ignore next */
  headerHtml => {
    var _content$parentNode;

    const content = document.body.firstChild;
    const headerContainer = document.createElement('div');
    headerContainer.className = 'reportWrapper';
    headerContainer.innerHTML = headerHtml;
    content === null || content === void 0 ? void 0 : (_content$parentNode = content.parentNode) === null || _content$parentNode === void 0 ? void 0 : _content$parentNode.insertBefore(headerContainer, content);
  }, headerHtml);
};

const addReportFooter = async (page, footer) => {
  const headerHtml = _fs.default.readFileSync(`${__dirname}/footer_template.html`).toString().replace('<!--CONTENT-->', footer);

  await page.evaluate(
  /* istanbul ignore next */
  headerHtml => {
    var _content$parentNode2;

    const content = document.body.firstChild;
    const headerContainer = document.createElement('div');
    headerContainer.className = 'reportWrapper';
    headerContainer.innerHTML = headerHtml;
    content === null || content === void 0 ? void 0 : (_content$parentNode2 = content.parentNode) === null || _content$parentNode2 === void 0 ? void 0 : _content$parentNode2.insertBefore(headerContainer, null);
  }, headerHtml);
}; // add waitForDynamicContent function


const waitForDynamicContent = async (page, timeout = 30000, interval = 1000, checks = 5) => {
  const maxChecks = timeout / interval;
  let passedChecks = 0;
  let previousLength = 0;
  let i = 0;

  while (i++ <= maxChecks) {
    let pageContent = await page.content();
    let currentLength = pageContent.length;
    previousLength === 0 || previousLength != currentLength ? passedChecks = 0 : passedChecks++;

    if (passedChecks >= checks) {
      break;
    }

    previousLength = currentLength;
    await page.waitFor(interval);
  }
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,[encoded data, 26268 characters]