text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.MetadataApiRetrieve = exports.RetrieveResult = void 0;
  /*
   * Copyright (c) 2021, salesforce.com, inc.
   * All rights reserved.
   * Licensed under the BSD 3-Clause license.
   * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
   */
  const path = require("path");
  const fs = require("graceful-fs");
  const unzipper = require("unzipper");
  const ts_types_1 = require("@salesforce/ts-types");
  const convert_1 = require("../convert");
  const collections_1 = require("../collections");
  const resolve_1 = require("../resolve");
  const errors_1 = require("../errors");
  const utils_1 = require("../utils");
  const metadataTransfer_1 = require("./metadataTransfer");
  const types_1 = require("./types");
  class RetrieveResult {
      /**
       * @param response The metadata retrieve response from the server
       * @param components The ComponentSet of retrieved source components
       * @param localComponents The ComponentSet used to create the retrieve request
       */
      constructor(response, components, localComponents) {
          this.response = response;
          this.components = components;
          this.localComponents = new collections_1.ComponentSet(localComponents === null || localComponents === void 0 ? void 0 : localComponents.getSourceComponents());
      }
      getFileResponses() {
          if (this.response && this.fileResponses) {
              return this.fileResponses;
          }
          this.fileResponses = [];
          // construct failures
          if (this.response.messages) {
              const retrieveMessages = (0, utils_1.normalizeToArray)(this.response.messages);
              for (const message of retrieveMessages) {
                  // match type name and fullname of problem component
                  const matches = new RegExp(/.+'(.+)'.+'(.+)'/).exec(message.problem);
                  if (matches) {
                      const [typeName, fullName] = matches.slice(1);
                      this.fileResponses.push({
                          fullName,
                          type: typeName,
                          state: types_1.ComponentStatus.Failed,
                          error: message.problem,
                          problemType: 'Error',
                      });
                  }
                  else {
                      this.fileResponses.push({
                          fullName: '',
                          type: '',
                          problemType: 'Error',
                          state: types_1.ComponentStatus.Failed,
                          error: message.problem,
                      });
                  }
              }
          }
          // construct successes
          for (const retrievedComponent of this.components.getSourceComponents()) {
              const { fullName, type, xml } = retrievedComponent;
              const baseResponse = {
                  fullName,
                  type: type.name,
                  state: this.localComponents.has(retrievedComponent) ? types_1.ComponentStatus.Changed : types_1.ComponentStatus.Created,
              };
              if (!type.children) {
                  for (const filePath of retrievedComponent.walkContent()) {
                      this.fileResponses.push(Object.assign({}, baseResponse, { filePath }));
                  }
              }
              if (xml) {
                  this.fileResponses.push(Object.assign({}, baseResponse, { filePath: xml }));
              }
          }
          return this.fileResponses;
      }
  }
  exports.RetrieveResult = RetrieveResult;
  class MetadataApiRetrieve extends metadataTransfer_1.MetadataTransfer {
      constructor(options) {
          super(options);
          this.options = Object.assign({}, MetadataApiRetrieve.DEFAULT_OPTIONS, options);
      }
      /**
       * Check the status of the retrieve operation.
       *
       * @returns Status of the retrieve
       */
      async checkStatus() {
          if (!this.id) {
              throw new errors_1.MissingJobIdError('retrieve');
          }
          const coerceBoolean = (field) => {
              if ((0, ts_types_1.isString)(field)) {
                  return field.toLowerCase() === 'true';
              }
              return (0, ts_types_1.asBoolean)(field, false);
          };
          const connection = await this.getConnection();
          // Cast RetrieveResult returned by jsForce to MetadataApiRetrieveStatus
          const status = (await connection.metadata.checkRetrieveStatus(this.id));
          status.fileProperties = (0, utils_1.normalizeToArray)(status.fileProperties);
          status.success = coerceBoolean(status.success);
          status.done = coerceBoolean(status.done);
          return status;
      }
      /**
       * Cancel the retrieve operation.
       *
       * Canceling a retrieve occurs immediately and requires no additional status
       * checks to the org, unlike {@link MetadataApiDeploy.cancel}.
       */
      // eslint-disable-next-line @typescript-eslint/require-await
      async cancel() {
          this.canceled = true;
      }
      async post(result) {
          let components;
          const isMdapiRetrieve = this.options.format === 'metadata';
          if (result.status === types_1.RequestStatus.Succeeded) {
              const zipFileContents = Buffer.from(result.zipFile, 'base64');
              if (isMdapiRetrieve) {
                  const name = this.options.zipFileName || 'unpackaged.zip';
                  const zipFilePath = path.join(this.options.output, name);
                  fs.writeFileSync(zipFilePath, zipFileContents);
                  if (this.options.unzip) {
                      const dir = await unzipper.Open.buffer(zipFileContents);
                      const extractPath = path.join(this.options.output, path.parse(name).name);
                      await dir.extract({ path: extractPath });
                  }
              }
              else {
                  components = await this.extract(zipFileContents);
              }
          }
          components !== null && components !== void 0 ? components : (components = new collections_1.ComponentSet(undefined, this.options.registry));
          if (!isMdapiRetrieve) {
              // This should only be done when retrieving source format since retrieving
              // mdapi format has no conversion.
              await this.maybeSaveTempDirectory('source', components);
          }
          return new RetrieveResult(result, components, this.components);
      }
      async pre() {
          const packageNames = this.getPackageNames();
          if (this.components.size === 0 && !(packageNames === null || packageNames === void 0 ? void 0 : packageNames.length)) {
              throw new errors_1.MetadataApiRetrieveError('error_no_components_to_retrieve');
          }
          const connection = await this.getConnection();
          const requestBody = {
              apiVersion: this.components.apiVersion,
              unpackaged: this.components.getObject().Package,
          };
          // if we're retrieving with packageNames add it
          // otherwise don't - it causes errors if undefined or an empty array
          if (packageNames === null || packageNames === void 0 ? void 0 : packageNames.length) {
              requestBody.packageNames = packageNames;
              // delete unpackaged when no components and metadata format to prevent
              // sending an empty unpackaged manifest.
              if (this.options.format === 'metadata' && this.components.size === 0) {
                  delete requestBody.unpackaged;
              }
          }
          if (this.options.singlePackage) {
              requestBody.singlePackage = this.options.singlePackage;
          }
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore required callback
          return connection.metadata.retrieve(requestBody);
      }
      getPackageNames() {
          var _a;
          return (_a = this.getPackageOptions()) === null || _a === void 0 ? void 0 : _a.map((pkg) => pkg.name);
      }
      getPackageOptions() {
          const { packageOptions } = this.options;
          if (packageOptions === null || packageOptions === void 0 ? void 0 : packageOptions.length) {
              if ((0, ts_types_1.isString)(packageOptions[0])) {
                  const packageNames = packageOptions;
                  return packageNames.map((pkg) => ({ name: pkg, outputDir: pkg }));
              }
              else {
                  const pkgs = packageOptions;
                  // If there isn't an outputDir specified, use the package name.
                  return pkgs.map(({ name, outputDir }) => ({ name, outputDir: outputDir || name }));
              }
          }
      }
      async extract(zip) {
          var _a;
          const components = [];
          const { merge, output, registry } = this.options;
          const converter = new convert_1.MetadataConverter(registry);
          const tree = await resolve_1.ZipTreeContainer.create(zip);
          const packages = [{ zipTreeLocation: 'unpackaged', outputDir: output }];
          const packageOpts = this.getPackageOptions();
          // eslint-disable-next-line no-unused-expressions
          packageOpts === null || packageOpts === void 0 ? void 0 : packageOpts.forEach(({ name, outputDir }) => {
              packages.push({ zipTreeLocation: name, outputDir });
          });
          for (const pkg of packages) {
              const outputConfig = merge
                  ? {
                      type: 'merge',
                      mergeWith: this.components.getSourceComponents(),
                      defaultDirectory: pkg.outputDir,
                      forceIgnoredPaths: (_a = this.components.forceIgnoredPaths) !== null && _a !== void 0 ? _a : new Set(),
                  }
                  : {
                      type: 'directory',
                      outputDirectory: pkg.outputDir,
                  };
              const zipComponents = collections_1.ComponentSet.fromSource({
                  fsPaths: [pkg.zipTreeLocation],
                  registry,
                  tree,
              })
                  .getSourceComponents()
                  .toArray();
              const convertResult = await converter.convert(zipComponents, 'source', outputConfig);
              if (convertResult) {
                  components.push(...convertResult.converted);
              }
          }
          return new collections_1.ComponentSet(components, registry);
      }
  }
  exports.MetadataApiRetrieve = MetadataApiRetrieve;
  MetadataApiRetrieve.DEFAULT_OPTIONS = { merge: false };
  //# sourceMappingURL=metadataApiRetrieve.js.map