UNPKG

@contentstack/cli-variants

Version:

Variants plugin

462 lines (461 loc) 30.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const omit_1 = __importDefault(require("lodash/omit")); const chunk_1 = __importDefault(require("lodash/chunk")); const entries_1 = __importDefault(require("lodash/entries")); const isEmpty_1 = __importDefault(require("lodash/isEmpty")); const forEach_1 = __importDefault(require("lodash/forEach")); const indexOf_1 = __importDefault(require("lodash/indexOf")); const path_1 = require("path"); const fs_1 = require("fs"); const cli_utilities_1 = require("@contentstack/cli-utilities"); const variant_api_adapter_1 = __importStar(require("../utils/variant-api-adapter")); const utils_1 = require("../utils"); class VariantEntries extends variant_api_adapter_1.default { constructor(config) { const conf = { config, httpClient: true, baseURL: config.host, Adapter: (variant_api_adapter_1.VariantHttpClient), headers: { api_key: config.apiKey, branch: config.branchName, organization_uid: config.org_uid, 'X-Project-Uid': config.modules.personalize.project_id, }, }; super(Object.assign((0, omit_1.default)(config, ['helpers']), conf)); this.config = config; this.entriesMapperPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(config.backupDir), 'mapper', 'entries'); this.personalizeConfig = this.config.modules.personalize; this.entriesDirPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(config.backupDir), (0, cli_utilities_1.sanitizePath)(config.modules.entries.dirName)); this.failedVariantPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.entriesMapperPath), 'failed-entry-variants.json'); this.failedVariantEntries = new Map(); if (this.config && this.config.context) { this.config.context.module = 'variant-entries'; } } /** * This TypeScript function asynchronously imports backupDir from a JSON file and processes the entries * for variant entries. * @returns If the file at the specified file path exists and contains backupDir, the function will parse * the JSON backupDir and iterate over each entry to import variant entries using the * `importVariantEntries` method. If the `entriesForVariants` array is empty, the function will log a * message indicating that no entries were found and return. */ import() { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const filePath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.entriesMapperPath), 'data-for-variant-entry.json'); const variantIdPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'mapper', (0, cli_utilities_1.sanitizePath)(this.personalizeConfig.dirName), (0, cli_utilities_1.sanitizePath)(this.personalizeConfig.experiences.dirName), 'variants-uid-mapping.json'); cli_utilities_1.log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context); if (!(0, fs_1.existsSync)(filePath)) { cli_utilities_1.log.warn(`Variant entry data file not found at path: ${filePath}`, this.config.context); return; } cli_utilities_1.log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context); if (!(0, fs_1.existsSync)(variantIdPath)) { cli_utilities_1.log.error('Variant UID mapping file not found', this.config.context); return; } const entriesForVariants = utils_1.fsUtil.readFile(filePath, true); cli_utilities_1.log.debug(`Loaded ${(entriesForVariants === null || entriesForVariants === void 0 ? void 0 : entriesForVariants.length) || 0} entries for variant processing`, this.config.context); if ((0, isEmpty_1.default)(entriesForVariants)) { cli_utilities_1.log.warn('No entries found for variant import', this.config.context); return; } const entriesUidMapperPath = (0, path_1.join)((0, cli_utilities_1.sanitizePath)(this.entriesMapperPath), 'uid-mapping.json'); const assetUidMapperPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'mapper', 'assets', 'uid-mapping.json'); const assetUrlMapperPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'mapper', 'assets', 'url-mapping.json'); const taxonomiesPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'mapper', (0, cli_utilities_1.sanitizePath)(this.config.modules.taxonomies.dirName), 'terms', 'success.json'); const marketplaceAppMapperPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'mapper', 'marketplace_apps', 'uid-mapping.json'); const envPath = (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), 'environments', 'environments.json'); cli_utilities_1.log.debug('Loading variant ID mapping and dependency data', this.config.context); // NOTE Read and store list of variant IDs this.variantIdList = (utils_1.fsUtil.readFile(variantIdPath, true) || {}); if ((0, isEmpty_1.default)(this.variantIdList)) { cli_utilities_1.log.warn('Empty variant UID data found', this.config.context); return; } // NOTE entry relational data lookup dependencies. this.entriesUidMapper = (utils_1.fsUtil.readFile(entriesUidMapperPath, true) || {}); this.installedExtensions = (utils_1.fsUtil.readFile(marketplaceAppMapperPath) || { extension_uid: {} }) .extension_uid; this.taxonomies = (utils_1.fsUtil.readFile(taxonomiesPath, true) || {}); this.assetUidMapper = (utils_1.fsUtil.readFile(assetUidMapperPath, true) || {}); this.assetUrlMapper = (utils_1.fsUtil.readFile(assetUrlMapperPath, true) || {}); this.environments = (utils_1.fsUtil.readFile(envPath, true) || {}); cli_utilities_1.log.debug(`Loaded dependency data - Entries: ${(_a = Object.keys(this.entriesUidMapper)) === null || _a === void 0 ? void 0 : _a.length}, Assets: ${(_b = Object.keys(this.assetUidMapper)) === null || _b === void 0 ? void 0 : _b.length}, Taxonomies: ${(_c = Object.keys(this.taxonomies)) === null || _c === void 0 ? void 0 : _c.length}`, this.config.context); // set the token yield this.variantInstance.init(); cli_utilities_1.log.info(`Processing ${entriesForVariants === null || entriesForVariants === void 0 ? void 0 : entriesForVariants.length} entries for variant import`, this.config.context); for (const entriesForVariant of entriesForVariants) { yield this.importVariantEntries(entriesForVariant); } cli_utilities_1.log.success('All variant entries have been imported and published successfully', this.config.context); }); } /** * The function `importVariantEntries` asynchronously imports variant entries using file system * utility in TypeScript. * @param {EntryDataForVariantEntries} entriesForVariant - EntryDataForVariantEntries { */ importVariantEntries(entriesForVariant) { return __awaiter(this, void 0, void 0, function* () { const variantEntry = this.config.modules.variantEntry; const { content_type, locale, entry_uid } = entriesForVariant; cli_utilities_1.log.info(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context); const ctConfig = this.config.modules['content-types']; const contentType = JSON.parse((0, fs_1.readFileSync)((0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.config.backupDir), (0, cli_utilities_1.sanitizePath)(ctConfig.dirName), `${(0, cli_utilities_1.sanitizePath)(content_type)}.json`), 'utf8')); const variantEntryBasePath = (0, path_1.join)((0, cli_utilities_1.sanitizePath)(this.entriesDirPath), (0, cli_utilities_1.sanitizePath)(content_type), (0, cli_utilities_1.sanitizePath)(locale), (0, cli_utilities_1.sanitizePath)(variantEntry.dirName), (0, cli_utilities_1.sanitizePath)(entry_uid)); cli_utilities_1.log.debug(`Processing variant entries from: ${variantEntryBasePath}`, this.config.context); const fs = new cli_utilities_1.FsUtility({ basePath: variantEntryBasePath, createDirIfNotExist: false }); for (const _ in fs.indexFileContent) { try { const variantEntries = (yield fs.readChunkFiles.next()); if (variantEntries === null || variantEntries === void 0 ? void 0 : variantEntries.length) { cli_utilities_1.log.info(`Processing batch of ${variantEntries.length} variant entries`, this.config.context); yield this.handleConcurrency(contentType, variantEntries, entriesForVariant); } } catch (error) { (0, cli_utilities_1.handleAndLogError)(error, this.config.context, `Failed to process variant entries for ${content_type}/${locale}/${entry_uid}`); } } }); } /** * The function `handleConcurrency` processes variant entries in batches with a specified concurrency * level and handles API calls for creating variant entries. * @param {VariantEntryStruct[]} variantEntries - The `variantEntries` parameter is an array of * `VariantEntryStruct` objects. It seems like this function is handling concurrency for processing * these entries in batches. The function chunks the `variantEntries` array into smaller batches and * then processes each batch asynchronously using `Promise.allSettled`. It also * @param {EntryDataForVariantEntries} entriesForVariant - The `entriesForVariant` parameter seems to * be an object containing the following properties: * @returns The `handleConcurrency` function processes variant entries in batches, creating variant * entries using the `createVariantEntry` method and handling the API response using * `Promise.allSettled`. The function also includes logic to handle variant IDs and delays between * batch processing. */ handleConcurrency(contentType, variantEntries, entriesForVariant) { return __awaiter(this, void 0, void 0, function* () { let batchNo = 0; const variantEntryConfig = this.config.modules.variantEntry; const { content_type, locale, entry_uid } = entriesForVariant; const entryUid = this.entriesUidMapper[entry_uid]; const batches = (0, chunk_1.default)(variantEntries, variantEntryConfig.apiConcurrency || 5); if ((0, isEmpty_1.default)(batches)) return; cli_utilities_1.log.debug(`Starting concurrent processing for ${variantEntries === null || variantEntries === void 0 ? void 0 : variantEntries.length} variant entries`, this.config.context); for (const [, batch] of (0, entries_1.default)(batches)) { batchNo += 1; const allPromise = []; const start = Date.now(); cli_utilities_1.log.debug(`Processing batch ${batchNo}/${batches === null || batches === void 0 ? void 0 : batches.length} with ${batch === null || batch === void 0 ? void 0 : batch.length} variant entries`, this.config.context); for (let [, variantEntry] of (0, entries_1.default)(batch)) { const onSuccess = ({ response, apiData: { entryUid, variantUid } }) => { cli_utilities_1.log.info(`Created entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`, this.config.context); }; const onReject = ({ error, apiData }) => { const { entryUid, variantUid } = apiData; this.failedVariantEntries.set(variantUid, apiData); (0, cli_utilities_1.handleAndLogError)(error, this.config.context, `Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`); }; // NOTE Find new variant Id by old Id const variantId = this.variantIdList[variantEntry._variant._uid]; cli_utilities_1.log.debug(`Looking up variant ID for ${variantEntry._variant._uid}: ${variantId ? 'found' : 'not found'}`, this.config.context); // NOTE Replace all the relation data UID's variantEntry = this.handleVariantEntryRelationalData(contentType, variantEntry); const changeSet = this.serializeChangeSet(variantEntry); const createVariantReq = Object.assign({ _variant: variantEntry._variant }, changeSet); if (variantId) { cli_utilities_1.log.debug(`Creating variant entry for variant ID: ${variantId}`, this.config.context); const promise = this.variantInstance.createVariantEntry(createVariantReq, { locale, entry_uid: entryUid, variant_id: variantId, content_type_uid: content_type, }, { reject: onReject.bind(this), resolve: onSuccess.bind(this), variantUid: variantId, }); allPromise.push(promise); } else { cli_utilities_1.log.error(`Variant ID not found for ${variantEntry._variant._uid}`, this.config.context); } } // NOTE Handle the API response here cli_utilities_1.log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context); yield Promise.allSettled(allPromise); cli_utilities_1.log.debug(`Batch ${batchNo} creation completed`, this.config.context); // NOTE publish all the entries yield this.publishVariantEntries(batch, entryUid, content_type); const end = Date.now(); const exeTime = end - start; cli_utilities_1.log.debug(`Batch ${batchNo} completed in ${exeTime}ms`, this.config.context); this.variantInstance.delay(1000 - exeTime); } cli_utilities_1.log.debug(`Writing failed variant entries to: ${this.failedVariantPath}`, this.config.context); utils_1.fsUtil.writeFile(this.failedVariantPath, this.failedVariantEntries); }); } /** * Serializes the change set of a entry variant. * @param variantEntry - The entry variant to serialize. * @returns The serialized change set as a record. */ serializeChangeSet(variantEntry) { var _a, _b; let changeSet = {}; if ((_b = (_a = variantEntry === null || variantEntry === void 0 ? void 0 : variantEntry._variant) === null || _a === void 0 ? void 0 : _a._change_set) === null || _b === void 0 ? void 0 : _b.length) { variantEntry._variant._change_set.forEach((key) => { key = key.split('.')[0]; if (variantEntry[key]) { changeSet[key] = variantEntry[key]; } }); } return changeSet; } /** * The function `handleVariantEntryRelationalData` processes relational data for a entry variant * based on the provided content type and configuration helpers. * @param {ContentTypeStruct} contentType - The `contentType` parameter in the * `handleVariantEntryRelationalData` function is of type `ContentTypeStruct`. It is used to define * the structure of the content type being processed within the function. This parameter likely * contains information about the schema and configuration of the content type. * @param {VariantEntryStruct} variantEntry - The `variantEntry` parameter in the * `handleVariantEntryRelationalData` function is a data structure that represents a entry variant. * It is of type `VariantEntryStruct` and contains information related to a specific entry variant. * This function is responsible for performing various operations on the `variantEntry` * @returns The function `handleVariantEntryRelationalData` returns the `variantEntry` after * performing various lookups and replacements on it based on the provided `contentType` and * `config.helpers`. */ handleVariantEntryRelationalData(contentType, variantEntry) { cli_utilities_1.log.debug(`Processing relational data for variant entry: ${variantEntry.uid}`, this.config.context); if (this.config.helpers) { const { lookUpTerms, lookupAssets, lookupExtension, lookupEntries, restoreJsonRteEntryRefs } = this.config.helpers; // FIXME Not sure why do we even need lookupExtension in entries [Ref taken from entries import] // Feel free to remove this flow if it's not valid // NOTE Find and replace extension's UID if (lookupExtension) { cli_utilities_1.log.debug('Processing extension lookups for variant entry', this.config.context); lookupExtension(this.config, contentType.schema, this.config.preserveStackVersion, this.installedExtensions); } // NOTE Find and replace RTE Ref UIDs cli_utilities_1.log.debug('Processing RTE reference lookups for variant entry', this.config.context); variantEntry = restoreJsonRteEntryRefs(variantEntry, variantEntry, contentType.schema, { uidMapper: this.entriesUidMapper, mappedAssetUids: this.assetUidMapper, mappedAssetUrls: this.assetUrlMapper, }); // NOTE Find and replace Entry Ref UIDs cli_utilities_1.log.debug('Processing entry reference lookups for variant entry', this.config.context); variantEntry = lookupEntries({ entry: variantEntry, content_type: contentType, }, this.entriesUidMapper, (0, path_1.resolve)((0, cli_utilities_1.sanitizePath)(this.entriesMapperPath), (0, cli_utilities_1.sanitizePath)(contentType.uid), (0, cli_utilities_1.sanitizePath)(variantEntry.locale))); // NOTE: will remove term if term doesn't exists in taxonomy // FIXME: Validate if taxonomy support available for variant entries, // if not, feel free to remove this lookup flow. cli_utilities_1.log.debug('Processing taxonomy term lookups for variant entry', this.config.context); lookUpTerms(contentType.schema, variantEntry, this.taxonomies, this.config); // update file fields of entry variants to support lookup asset logic cli_utilities_1.log.debug('Updating file fields for variant entry', this.config.context); this.updateFileFields(variantEntry); // NOTE Find and replace asset's UID cli_utilities_1.log.debug('Processing asset lookups for variant entry', this.config.context); variantEntry = lookupAssets({ entry: variantEntry, content_type: contentType, }, this.assetUidMapper, this.assetUrlMapper, (0, path_1.join)((0, cli_utilities_1.sanitizePath)(this.entriesDirPath), (0, cli_utilities_1.sanitizePath)(contentType.uid)), this.installedExtensions); } return variantEntry; } /** * Updates the file fields of a entry variant to support lookup asset logic. * Lookup asset expects file fields to be an object instead of a string. So here we are updating the file fields to be an object. Object has two keys: `uid` and `filename`. `uid` is the asset UID and `filename` is the name of the file. Used a dummy value for the filename. This is a temporary fix and will be updated in the future. * @param variantEntry - The entry variant to update. */ updateFileFields(variantEntry) { var _a, _b; cli_utilities_1.log.debug(`Updating file fields for variant entry: ${variantEntry.uid}`, this.config.context); const setValue = (currentObj, keys) => { if (!currentObj || keys.length === 0) return; const [firstKey, ...restKeys] = keys; if (Array.isArray(currentObj)) { for (const item of currentObj) { setValue(item, [firstKey, ...restKeys]); } } else if (currentObj && typeof currentObj === 'object') { if (firstKey in currentObj) { if (keys.length === 1) { // Check if the current property is already an object with uid and filename const existingValue = currentObj[firstKey]; if (existingValue && typeof existingValue === 'object' && existingValue.uid) { currentObj[firstKey] = { uid: existingValue.uid, filename: 'dummy.jpeg' }; } else { currentObj[firstKey] = { uid: currentObj[firstKey], filename: 'dummy.jpeg' }; } } else { setValue(currentObj[firstKey], restKeys); } } } }; const pathsToUpdate = ((_b = (_a = variantEntry === null || variantEntry === void 0 ? void 0 : variantEntry._metadata) === null || _a === void 0 ? void 0 : _a.references) === null || _b === void 0 ? void 0 : _b.filter((ref) => ref._content_type_uid === 'sys_assets').map((ref) => ref.path)) || []; cli_utilities_1.log.debug(`Found ${pathsToUpdate === null || pathsToUpdate === void 0 ? void 0 : pathsToUpdate.length} file field paths to update`, this.config.context); pathsToUpdate.forEach((path) => setValue(variantEntry, path.split('.'))); } /** * Publishes variant entries in batch for a given entry UID and content type. * @param batch - An array of VariantEntryStruct objects representing the variant entries to be published. * @param entryUid - The UID of the entry for which the variant entries are being published. * @param content_type - The UID of the content type of the entry. * @returns A Promise that resolves when all variant entries have been published. */ publishVariantEntries(batch, entryUid, content_type) { return __awaiter(this, void 0, void 0, function* () { var _a; const allPromise = []; cli_utilities_1.log.info(`Publishing variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`, this.config.context); cli_utilities_1.log.debug(`Processing ${batch.length} variant entries for publishing`, this.config.context); for (let [, variantEntry] of (0, entries_1.default)(batch)) { const variantEntryUID = variantEntry.uid; const oldVariantUid = variantEntry._variant._uid || ''; const newVariantUid = this.variantIdList[oldVariantUid]; if (!newVariantUid) { cli_utilities_1.log.debug('Variant ID not found', this.config.context); continue; } if (this.failedVariantEntries.has(variantEntryUID)) { cli_utilities_1.log.info(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context); continue; } if ((_a = this.environments) === null || _a === void 0 ? void 0 : _a.length) { cli_utilities_1.log.info('No environments found. Skipping entry variant publishing...', this.config.context); return; } const onSuccess = ({ response, apiData: { entryUid, variantUid } }) => { cli_utilities_1.log.info(`Entry variant: '${variantUid}' of entry '${entryUid}' published on locales '${locales.join(',')}'`, this.config.context); }; const onReject = ({ error, apiData: { entryUid, variantUid } }) => { (0, cli_utilities_1.handleAndLogError)(error, this.config.context, `Failed to publish entry variant: '${variantUid}' of entry uid ${entryUid} on locales '${locales.join(',')}'`); }; const { environments, locales } = this.serializePublishEntries(variantEntry); if ((environments === null || environments === void 0 ? void 0 : environments.length) === 0 || (locales === null || locales === void 0 ? void 0 : locales.length) === 0) { cli_utilities_1.log.info(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context); continue; } cli_utilities_1.log.debug(`Publishing variant ${newVariantUid} to environments: ${environments.join(', ')}, locales: ${locales.join(', ')}`, this.config.context); const publishReq = { entry: { environments, locales, variants: [{ uid: newVariantUid, version: 1 }], }, locale: variantEntry.locale, }; const promise = this.variantInstance.publishVariantEntry(publishReq, { entry_uid: entryUid, content_type_uid: content_type, }, { reject: onReject.bind(this), resolve: onSuccess.bind(this), log: cli_utilities_1.log, variantUid: newVariantUid, }); allPromise.push(promise); } yield Promise.allSettled(allPromise); cli_utilities_1.log.info(`Published variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`, this.config.context); }); } /** * Serializes the publish entries of a variant. * @param variantEntry - The entry variant to serialize. * @returns An object containing the serialized publish entries. */ serializePublishEntries(variantEntry) { var _a, _b; cli_utilities_1.log.debug(`Serializing publish entries for variant: ${variantEntry.uid}`, this.config.context); const requestObject = { environments: [], locales: [], }; if (variantEntry.publish_details && ((_a = variantEntry.publish_details) === null || _a === void 0 ? void 0 : _a.length) > 0) { cli_utilities_1.log.debug(`Processing ${variantEntry.publish_details.length} publish details`, this.config.context); } else { cli_utilities_1.log.debug('No publish details found for variant entry.', this.config.context); } if (variantEntry.publish_details && ((_b = variantEntry.publish_details) === null || _b === void 0 ? void 0 : _b.length) > 0) { (0, forEach_1.default)(variantEntry.publish_details, (pubObject) => { if (this.environments.hasOwnProperty(pubObject.environment) && (0, indexOf_1.default)(requestObject.environments, this.environments[pubObject.environment].name) === -1) { requestObject.environments.push(this.environments[pubObject.environment].name); } if (pubObject.locale && (0, indexOf_1.default)(requestObject.locales, pubObject.locale) === -1) { requestObject.locales.push(pubObject.locale); } }); } cli_utilities_1.log.debug(`Serialized publish data - environments: ${requestObject.environments.length}, locales: ${requestObject.locales.length}`, this.config.context); return requestObject; } } exports.default = VariantEntries;