"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.registerExportRoute = void 0;
var _configSchema = require("@kbn/config-schema");
var _jsonStableStringify = _interopRequireDefault(require("json-stable-stringify"));
var _utils = require("@kbn/utils");
var _coreSavedObjectsImportExportServerInternal = require("@kbn/core-saved-objects-import-export-server-internal");
var _utils2 = require("./utils");
/*
 * 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 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

const cleanOptions = ({
  type,
  objects,
  search,
  hasReference,
  excludeExportDetails,
  includeReferencesDeep
}) => {
  return {
    types: typeof type === 'string' ? [type] : type,
    search,
    objects,
    hasReference: hasReference && !Array.isArray(hasReference) ? [hasReference] : hasReference,
    excludeExportDetails,
    includeReferencesDeep
  };
};
const isExportByTypeOptions = options => {
  return Boolean(options.types);
};
const validateOptions = ({
  types,
  objects,
  excludeExportDetails,
  hasReference,
  includeReferencesDeep,
  search
}, {
  exportSizeLimit,
  supportedTypes,
  request
}) => {
  var _types$length, _objects$length;
  const hasTypes = ((_types$length = types === null || types === void 0 ? void 0 : types.length) !== null && _types$length !== void 0 ? _types$length : 0) > 0;
  const hasObjects = ((_objects$length = objects === null || objects === void 0 ? void 0 : objects.length) !== null && _objects$length !== void 0 ? _objects$length : 0) > 0;
  if (!hasTypes && !hasObjects) {
    throw new Error('Either `type` or `objects` are required.');
  }
  if (hasTypes && hasObjects) {
    throw new Error(`Can't specify both "types" and "objects" properties when exporting`);
  }
  if (hasObjects) {
    if (objects.length > exportSizeLimit) {
      throw new Error(`Can't export more than ${exportSizeLimit} objects`);
    }
    if (typeof search === 'string') {
      throw new Error(`Can't specify both "search" and "objects" properties when exporting`);
    }
    if (hasReference && hasReference.length) {
      throw new Error(`Can't specify both "references" and "objects" properties when exporting`);
    }
    const validationError = (0, _utils2.validateObjects)(objects, supportedTypes);
    if (validationError) {
      throw new Error(validationError);
    }
    return {
      objects: objects,
      excludeExportDetails,
      includeReferencesDeep,
      request
    };
  } else {
    const validationError = (0, _utils2.validateTypes)(types, supportedTypes);
    if (validationError) {
      throw new Error(validationError);
    }
    return {
      types: types,
      hasReference,
      search,
      excludeExportDetails,
      includeReferencesDeep,
      request
    };
  }
};
const registerExportRoute = (router, {
  config,
  coreUsageData
}) => {
  const {
    maxImportExportSize
  } = config;
  const referenceSchema = _configSchema.schema.object({
    type: _configSchema.schema.string(),
    id: _configSchema.schema.string()
  });
  router.post({
    path: '/_export',
    validate: {
      body: _configSchema.schema.object({
        type: _configSchema.schema.maybe(_configSchema.schema.oneOf([_configSchema.schema.string(), _configSchema.schema.arrayOf(_configSchema.schema.string())])),
        hasReference: _configSchema.schema.maybe(_configSchema.schema.oneOf([referenceSchema, _configSchema.schema.arrayOf(referenceSchema)])),
        objects: _configSchema.schema.maybe(_configSchema.schema.arrayOf(_configSchema.schema.object({
          type: _configSchema.schema.string(),
          id: _configSchema.schema.string()
        }), {
          maxSize: maxImportExportSize
        })),
        search: _configSchema.schema.maybe(_configSchema.schema.string()),
        includeReferencesDeep: _configSchema.schema.boolean({
          defaultValue: false
        }),
        excludeExportDetails: _configSchema.schema.boolean({
          defaultValue: false
        })
      })
    }
  }, (0, _utils2.catchAndReturnBoomErrors)(async (context, req, res) => {
    const cleaned = cleanOptions(req.body);
    const {
      typeRegistry,
      getExporter,
      getClient
    } = (await context.core).savedObjects;
    const supportedTypes = typeRegistry.getImportableAndExportableTypes().map(t => t.name);
    let options;
    try {
      options = validateOptions(cleaned, {
        request: req,
        exportSizeLimit: maxImportExportSize,
        supportedTypes
      });
    } catch (e) {
      return res.badRequest({
        body: e
      });
    }
    const includedHiddenTypes = supportedTypes.filter(supportedType => typeRegistry.isHidden(supportedType));
    const client = getClient({
      includedHiddenTypes
    });
    const exporter = getExporter(client);
    const usageStatsClient = coreUsageData.getClient();
    usageStatsClient.incrementSavedObjectsExport({
      request: req,
      types: cleaned.types,
      supportedTypes
    }).catch(() => {});
    try {
      const exportStream = isExportByTypeOptions(options) ? await exporter.exportByTypes(options) : await exporter.exportByObjects(options);
      const docsToExport = await (0, _utils.createPromiseFromStreams)([exportStream, (0, _utils.createMapStream)(obj => {
        return (0, _jsonStableStringify.default)(obj);
      }), (0, _utils.createConcatStream)([])]);
      return res.ok({
        body: docsToExport.join('\n'),
        headers: {
          'Content-Disposition': `attachment; filename="export.ndjson"`,
          'Content-Type': 'application/ndjson'
        }
      });
    } catch (e) {
      if (e instanceof _coreSavedObjectsImportExportServerInternal.SavedObjectsExportError) {
        return res.badRequest({
          body: {
            message: e.message,
            attributes: e.attributes
          }
        });
      }
      throw e;
    }
  }));
};
exports.registerExportRoute = registerExportRoute;