"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SavedObjectsService = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _rxjs = require("rxjs");
var _operators = require("rxjs/operators");
var _std = require("@kbn/std");
var _coreSavedObjectsBaseServerInternal = require("@kbn/core-saved-objects-base-server-internal");
var _coreSavedObjectsApiServerInternal = require("@kbn/core-saved-objects-api-server-internal");
var _coreSavedObjectsMigrationServerInternal = require("@kbn/core-saved-objects-migration-server-internal");
var _coreSavedObjectsImportExportServerInternal = require("@kbn/core-saved-objects-import-export-server-internal");
var _coreSavedObjectsServer = require("@kbn/core-saved-objects-server");
var _routes = require("./routes");
var _status = require("./status");
var _object_types = require("./object_types");
var _deprecations = require("./deprecations");
var _apply_type_defaults = require("./apply_type_defaults");
var _utils = 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.
 */

/**
 * @internal
 */

/** @internal */

/** @internal */

class SavedObjectsService {
  constructor(coreContext) {
    (0, _defineProperty2.default)(this, "logger", void 0);
    (0, _defineProperty2.default)(this, "kibanaVersion", void 0);
    (0, _defineProperty2.default)(this, "setupDeps", void 0);
    (0, _defineProperty2.default)(this, "config", void 0);
    (0, _defineProperty2.default)(this, "clientFactoryProvider", void 0);
    (0, _defineProperty2.default)(this, "encryptionExtensionFactory", void 0);
    (0, _defineProperty2.default)(this, "securityExtensionFactory", void 0);
    (0, _defineProperty2.default)(this, "spacesExtensionFactory", void 0);
    (0, _defineProperty2.default)(this, "migrator$", new _rxjs.Subject());
    (0, _defineProperty2.default)(this, "typeRegistry", new _coreSavedObjectsBaseServerInternal.SavedObjectTypeRegistry());
    (0, _defineProperty2.default)(this, "started", false);
    this.coreContext = coreContext;
    this.logger = coreContext.logger.get('savedobjects-service');
    this.kibanaVersion = (0, _std.stripVersionQualifier)(this.coreContext.env.packageInfo.version);
  }
  async setup(setupDeps) {
    this.logger.debug('Setting up SavedObjects service');
    this.setupDeps = setupDeps;
    const {
      http,
      elasticsearch,
      coreUsageData,
      deprecations
    } = setupDeps;
    const savedObjectsConfig = await (0, _rxjs.firstValueFrom)(this.coreContext.configService.atPath('savedObjects'));
    const savedObjectsMigrationConfig = await (0, _rxjs.firstValueFrom)(this.coreContext.configService.atPath('migrations'));
    this.config = new _coreSavedObjectsBaseServerInternal.SavedObjectConfig(savedObjectsConfig, savedObjectsMigrationConfig);
    deprecations.getRegistry('savedObjects').registerDeprecations((0, _deprecations.getSavedObjectsDeprecationsProvider)({
      kibanaIndex: _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX,
      savedObjectsConfig: this.config,
      kibanaVersion: this.kibanaVersion,
      typeRegistry: this.typeRegistry
    }));
    coreUsageData.registerType(this.typeRegistry);
    (0, _routes.registerRoutes)({
      http,
      coreUsageData,
      logger: this.logger,
      config: this.config,
      migratorPromise: (0, _rxjs.firstValueFrom)(this.migrator$),
      kibanaIndex: _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX,
      kibanaVersion: this.kibanaVersion
    });
    (0, _object_types.registerCoreObjectTypes)(this.typeRegistry);
    const skipMigration = this.config.migration.skip;
    return {
      status$: (0, _status.calculateStatus$)(skipMigration ? (0, _rxjs.of)({
        status: 'completed'
      }) : this.migrator$.pipe((0, _operators.switchMap)(migrator => migrator.getStatus$())), elasticsearch.status$),
      setClientFactoryProvider: provider => {
        if (this.started) {
          throw new Error('cannot call `setClientFactoryProvider` after service startup.');
        }
        if (this.clientFactoryProvider) {
          throw new Error('custom client factory is already set, and can only be set once');
        }
        this.clientFactoryProvider = provider;
      },
      setEncryptionExtension: factory => {
        if (this.started) {
          throw new Error('cannot call `setEncryptionExtension` after service startup.');
        }
        if (this.encryptionExtensionFactory) {
          throw new Error('encryption extension is already set, and can only be set once');
        }
        this.encryptionExtensionFactory = factory;
      },
      setSecurityExtension: factory => {
        if (this.started) {
          throw new Error('cannot call `setSecurityExtension` after service startup.');
        }
        if (this.securityExtensionFactory) {
          throw new Error('security extension is already set, and can only be set once');
        }
        this.securityExtensionFactory = factory;
      },
      setSpacesExtension: factory => {
        if (this.started) {
          throw new Error('cannot call `setSpacesExtension` after service startup.');
        }
        if (this.spacesExtensionFactory) {
          throw new Error('spaces extension is already set, and can only be set once');
        }
        this.spacesExtensionFactory = factory;
      },
      registerType: type => {
        if (this.started) {
          throw new Error('cannot call `registerType` after service startup.');
        }
        this.typeRegistry.registerType((0, _apply_type_defaults.applyTypeDefaults)(type));
      },
      getTypeRegistry: () => this.typeRegistry,
      getDefaultIndex: () => _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX
    };
  }
  async start({
    elasticsearch,
    pluginsInitialized = true,
    docLinks,
    node
  }) {
    if (!this.setupDeps || !this.config) {
      throw new Error('#setup() needs to be run first');
    }
    this.logger.debug('Starting SavedObjects service');
    const client = elasticsearch.client;
    const waitForMigrationCompletion = node.roles.backgroundTasks && !node.roles.ui;
    const migrator = this.createMigrator(this.config.migration, elasticsearch.client.asInternalUser, docLinks, waitForMigrationCompletion, node, elasticsearch.getCapabilities());
    this.migrator$.next(migrator);

    /**
     * Note: We want to ensure that migrations have completed before
     * continuing with further Core start steps that might use SavedObjects
     * such as running the legacy server, legacy plugins and allowing incoming
     * HTTP requests.
     *
     * However, our build system optimize step and some tests depend on the
     * HTTP server running without an Elasticsearch server being available.
     * So, when the `migrations.skip` is true, we skip migrations altogether.
     *
     * We also cannot safely run migrations if plugins are not initialized since
     * not plugin migrations won't be registered.
     */
    const skipMigrations = this.config.migration.skip || !pluginsInitialized;

    /**
     * Note: Prepares all migrations maps. If a saved object type was registered with property `migrations`
     * of type function; this function will be called to get the type's SavedObjectMigrationMap.
     */
    migrator.prepareMigrations();
    if (skipMigrations) {
      this.logger.warn('Skipping Saved Object migrations on startup. Note: Individual documents will still be migrated when read or written.');
    } else {
      this.logger.info('Waiting until all Elasticsearch nodes are compatible with Kibana before starting saved objects migrations...');

      // The Elasticsearch service should already ensure that, but let's double check just in case.
      // Should it be replaced with elasticsearch.status$ API instead?
      const compatibleNodes = await this.setupDeps.elasticsearch.esNodesCompatibility$.pipe((0, _operators.filter)(nodes => nodes.isCompatible), (0, _operators.take)(1)).toPromise();

      // Running migrations only if we got compatible nodes.
      // It may happen that the observable completes due to Kibana shutting down
      // and the promise above fulfils as undefined. We shouldn't trigger migrations at that point.
      if (compatibleNodes) {
        this.logger.info('Starting saved objects migrations');
        await migrator.runMigrations();
      }
    }
    const createRepository = (esClient, includedHiddenTypes = [], extensions) => {
      return _coreSavedObjectsApiServerInternal.SavedObjectsRepository.createRepository(migrator, this.typeRegistry, _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX, esClient, this.logger.get('repository'), includedHiddenTypes, extensions);
    };
    const repositoryFactory = {
      createInternalRepository: (includedHiddenTypes, extensions) => createRepository(client.asInternalUser, includedHiddenTypes, extensions),
      createScopedRepository: (req, includedHiddenTypes, extensions) => createRepository(client.asScoped(req).asCurrentUser, includedHiddenTypes, extensions)
    };
    const clientProvider = new _coreSavedObjectsApiServerInternal.SavedObjectsClientProvider({
      defaultClientFactory({
        request,
        includedHiddenTypes,
        extensions
      }) {
        const repository = repositoryFactory.createScopedRepository(request, includedHiddenTypes, extensions);
        return new _coreSavedObjectsApiServerInternal.SavedObjectsClient(repository);
      },
      typeRegistry: this.typeRegistry,
      encryptionExtensionFactory: this.encryptionExtensionFactory,
      securityExtensionFactory: this.securityExtensionFactory,
      spacesExtensionFactory: this.spacesExtensionFactory
    });
    if (this.clientFactoryProvider) {
      const clientFactory = this.clientFactoryProvider(repositoryFactory);
      clientProvider.setClientFactory(clientFactory);
    }
    const allIndices = (0, _utils.getAllIndices)({
      registry: this.typeRegistry
    });
    this.started = true;
    return {
      getScopedClient: clientProvider.getClient.bind(clientProvider),
      createScopedRepository: repositoryFactory.createScopedRepository,
      createInternalRepository: repositoryFactory.createInternalRepository,
      createSerializer: () => new _coreSavedObjectsBaseServerInternal.SavedObjectsSerializer(this.typeRegistry),
      createExporter: savedObjectsClient => new _coreSavedObjectsImportExportServerInternal.SavedObjectsExporter({
        savedObjectsClient,
        typeRegistry: this.typeRegistry,
        exportSizeLimit: this.config.maxImportExportSize,
        logger: this.logger.get('exporter')
      }),
      createImporter: (savedObjectsClient, options) => {
        var _options$importSizeLi;
        return new _coreSavedObjectsImportExportServerInternal.SavedObjectsImporter({
          savedObjectsClient,
          typeRegistry: this.typeRegistry,
          importSizeLimit: (_options$importSizeLi = options === null || options === void 0 ? void 0 : options.importSizeLimit) !== null && _options$importSizeLi !== void 0 ? _options$importSizeLi : this.config.maxImportExportSize
        });
      },
      getTypeRegistry: () => this.typeRegistry,
      getDefaultIndex: () => _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX,
      getIndexForType: type => {
        var _definition$indexPatt;
        const definition = this.typeRegistry.getType(type);
        return (_definition$indexPatt = definition === null || definition === void 0 ? void 0 : definition.indexPattern) !== null && _definition$indexPatt !== void 0 ? _definition$indexPatt : _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX;
      },
      getIndicesForTypes: types => {
        const indices = new Set();
        types.forEach(type => {
          var _definition$indexPatt2;
          const definition = this.typeRegistry.getType(type);
          const index = (_definition$indexPatt2 = definition === null || definition === void 0 ? void 0 : definition.indexPattern) !== null && _definition$indexPatt2 !== void 0 ? _definition$indexPatt2 : _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX;
          indices.add(index);
        });
        return [...indices];
      },
      getAllIndices: () => [...allIndices]
    };
  }
  async stop() {}
  createMigrator(soMigrationsConfig, client, docLinks, waitForMigrationCompletion, nodeInfo, esCapabilities) {
    return new _coreSavedObjectsMigrationServerInternal.KibanaMigrator({
      typeRegistry: this.typeRegistry,
      logger: this.logger,
      kibanaVersion: this.kibanaVersion,
      soMigrationsConfig,
      kibanaIndex: _coreSavedObjectsServer.MAIN_SAVED_OBJECT_INDEX,
      defaultIndexTypesMap: _coreSavedObjectsBaseServerInternal.DEFAULT_INDEX_TYPES_MAP,
      client,
      docLinks,
      waitForMigrationCompletion,
      nodeRoles: nodeInfo.roles,
      esCapabilities
    });
  }
}
exports.SavedObjectsService = SavedObjectsService;