diff --git a/package-lock.json b/package-lock.json index 5f53fc96d5..969f4cc9ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24301,7 +24301,7 @@ }, "packages/contentstack": { "name": "@contentstack/cli", - "version": "1.22.0", + "version": "1.23.0", "license": "MIT", "dependencies": { "@contentstack/cli-audit": "~1.6.5", @@ -24317,7 +24317,7 @@ "@contentstack/cli-cm-seed": "~1.7.8", "@contentstack/cli-command": "~1.2.19", "@contentstack/cli-config": "~1.6.5", - "@contentstack/cli-launch": "~1.1.0", + "@contentstack/cli-launch": "~1.2.0", "@contentstack/cli-migration": "~1.6.1", "@contentstack/cli-utilities": "~1.7.1", "@contentstack/management": "~1.17.0", @@ -26102,7 +26102,7 @@ }, "packages/contentstack-launch": { "name": "@contentstack/cli-launch", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@apollo/client": "^3.7.9", diff --git a/packages/contentstack-launch/README.md b/packages/contentstack-launch/README.md index 4b32cc5840..0a56303813 100755 --- a/packages/contentstack-launch/README.md +++ b/packages/contentstack-launch/README.md @@ -19,7 +19,7 @@ $ npm install -g @contentstack/cli-launch $ csdx COMMAND running command... $ csdx (--version|-v) -@contentstack/cli-launch/1.1.0 darwin-arm64 node-v22.2.0 +@contentstack/cli-launch/1.2.0 darwin-arm64 node-v22.2.0 $ csdx --help [COMMAND] USAGE $ csdx COMMAND diff --git a/packages/contentstack-launch/package.json b/packages/contentstack-launch/package.json index 0e05a6c271..3a7b8ad78a 100755 --- a/packages/contentstack-launch/package.json +++ b/packages/contentstack-launch/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/cli-launch", - "version": "1.1.0", + "version": "1.2.0", "description": "Launch related operations", "author": "Contentstack CLI", "bin": { diff --git a/packages/contentstack-launch/src/adapters/file-upload.ts b/packages/contentstack-launch/src/adapters/file-upload.ts index 2cc9c9f969..dafd13a68d 100755 --- a/packages/contentstack-launch/src/adapters/file-upload.ts +++ b/packages/contentstack-launch/src/adapters/file-upload.ts @@ -5,8 +5,9 @@ import find from 'lodash/find'; import FormData from 'form-data'; import filter from 'lodash/filter'; import includes from 'lodash/includes'; +import isEmpty from 'lodash/isEmpty'; import { basename, resolve } from 'path'; -import { cliux, configHandler, ux } from '@contentstack/cli-utilities'; +import { cliux, configHandler, HttpClient, ux } from '@contentstack/cli-utilities'; import { createReadStream, existsSync, PathLike, statSync } from 'fs'; import { print } from '../util'; @@ -263,28 +264,80 @@ export default class FileUpload extends BaseClass { * @memberof FileUpload */ async uploadFile(fileName: string, filePath: PathLike): Promise { - const { uploadUrl, fields } = this.signedUploadUrlData; + const { uploadUrl, fields, headers } = this.signedUploadUrlData; const formData = new FormData(); - for (const { formFieldKey, formFieldValue } of fields) { - formData.append(formFieldKey, formFieldValue); + if (!isEmpty(fields)) { + for (const { formFieldKey, formFieldValue } of fields) { + formData.append(formFieldKey, formFieldValue); + } + + formData.append('file', createReadStream(filePath), fileName); + await this.submitFormData(formData, uploadUrl); + } else if (!isEmpty(headers)) { + await this.uploadWithHttpClient(filePath, uploadUrl, headers, fileName); } + } - formData.append('file', createReadStream(filePath) as any, fileName); + private async submitFormData(formData: FormData, uploadUrl: string): Promise { + ux.action.start('Starting file upload...'); + try { + await new Promise((resolve, reject) => { + formData.submit(uploadUrl, (error, res) => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); - await new Promise((resolve) => { - ux.action.start('Starting file upload...'); - formData.submit(uploadUrl, (error, res) => { - if (error) { - ux.action.stop('File upload failed!'); - this.log('File upload failed. Please try again.', 'error'); - this.log(error, 'error'); - this.exit(1); - } + ux.action.stop(); + } catch (error) { + ux.action.stop('File upload failed!'); + this.log('File upload failed. Please try again.', 'error'); + if (error instanceof Error) { + this.log(error.message, 'error'); + } + this.exit(1); + } + } + + private async uploadWithHttpClient( + filePath: PathLike, + uploadUrl: string, + headers: Array<{ key: string; value: string }>, + fileName: string, + ): Promise { + ux.action.start('Starting file upload...'); + const httpClient = new HttpClient(); + const form = new FormData(); + form.append('file', createReadStream(filePath), fileName); - resolve(); + // Convert headers array to a headers object + const headerObject = headers.reduce((acc, { key, value }) => { + acc[key] = value; + return acc; + }, {} as Record); + + try { + const response = await httpClient.headers(headerObject).put(uploadUrl, form); + const { status } = response; + + if (status >= 200 && status < 300) { ux.action.stop(); - }); - }); + } else { + ux.action.stop('File upload failed!'); + this.log('File upload failed. Please try again.', 'error'); + this.exit(1); + } + } catch (error) { + ux.action.stop('File upload failed!'); + this.log('File upload failed. Please try again.', 'error'); + if (error instanceof Error) { + this.log(`Error: ${error.message}`, 'error'); + } + this.exit(1); + } } } diff --git a/packages/contentstack-launch/src/base-command.ts b/packages/contentstack-launch/src/base-command.ts index 018ef3ecde..f37d5bfdbc 100755 --- a/packages/contentstack-launch/src/base-command.ts +++ b/packages/contentstack-launch/src/base-command.ts @@ -18,7 +18,7 @@ import { } from '@contentstack/cli-utilities'; import config from './config'; -import { GraphqlApiClient, Logger } from './util'; +import { getLaunchHubUrl, GraphqlApiClient, Logger } from './util'; import { ConfigType, LogFn, Providers } from './types'; export type Flags = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>; @@ -105,7 +105,7 @@ export abstract class BaseCommand extends Command { this.flags['data-dir'] || this.flags.config ? this.flags.config || resolve(this.flags['data-dir'], config.configName) : resolve(process.cwd(), config.configName); - const baseUrl = config.launchBaseUrl || (config.launchHubUrls as Record)[this.cmaAPIUrl]; + const baseUrl = config.launchBaseUrl || getLaunchHubUrl(); this.sharedConfig = { ...require('./config').default, currentConfig: {}, diff --git a/packages/contentstack-launch/src/config/index.ts b/packages/contentstack-launch/src/config/index.ts index 6ac33c697a..a2c4d3180b 100755 --- a/packages/contentstack-launch/src/config/index.ts +++ b/packages/contentstack-launch/src/config/index.ts @@ -29,13 +29,7 @@ const config = { GITPROVIDER: 'GitHub', FILEUPLOAD: 'FileUpload', }, - launchHubUrls: { - // NOTE CMA url used as launch url mapper to avoid conflict if user used any custom name - 'https://api.contentstack.io': 'https://launch-api.contentstack.com', - 'https://eu-api.contentstack.com': '', - 'https://azure-na-api.contentstack.com': '', - 'https://azure-eu-api.contentstack.com': '', - }, + launchHubUrls: '', launchBaseUrl: '', supportedAdapters: ['GitHub'], deploymentStatus: ['LIVE', 'FAILED', 'SKIPPED', 'DEPLOYED'], @@ -45,7 +39,7 @@ const config = { 'Manually add custom variables to the list', 'Import variables from the local env file', ], - variableType: '' + variableType: '', }; export default config; diff --git a/packages/contentstack-launch/src/graphql/mutation.ts b/packages/contentstack-launch/src/graphql/mutation.ts index 3edc731f62..0cab22b360 100755 --- a/packages/contentstack-launch/src/graphql/mutation.ts +++ b/packages/contentstack-launch/src/graphql/mutation.ts @@ -28,6 +28,10 @@ const createSignedUploadUrlMutation: DocumentNode = gql` formFieldKey formFieldValue } + headers{ + key + value + } } } `; diff --git a/packages/contentstack-launch/src/util/common-utility.ts b/packages/contentstack-launch/src/util/common-utility.ts index 08c4d18a40..71cfc43cb1 100644 --- a/packages/contentstack-launch/src/util/common-utility.ts +++ b/packages/contentstack-launch/src/util/common-utility.ts @@ -133,4 +133,22 @@ async function selectProject(options: { } } -export { getOrganizations, selectOrg, selectProject }; +function getLaunchHubUrl(): string { + const { cma } = configHandler.get('region') || {}; + if (!cma) { + throw new Error('Region not configured. Please set the region with command $ csdx config:set:region'); + } + + let launchHubBaseUrl = cma.replace('api', 'launch-api'); + + if (launchHubBaseUrl.startsWith('http')) { + launchHubBaseUrl = launchHubBaseUrl.split('//')[1]; + } + + launchHubBaseUrl = launchHubBaseUrl.startsWith('dev11') ? launchHubBaseUrl.replace('dev11', 'dev') : launchHubBaseUrl; + launchHubBaseUrl = launchHubBaseUrl.endsWith('io') ? launchHubBaseUrl.replace('io', 'com') : launchHubBaseUrl; + + return `https://${launchHubBaseUrl}`; +} + +export { getOrganizations, selectOrg, selectProject, getLaunchHubUrl }; diff --git a/packages/contentstack-launch/src/util/logs-polling-utilities.ts b/packages/contentstack-launch/src/util/logs-polling-utilities.ts index 150e365ded..735db0b7ca 100755 --- a/packages/contentstack-launch/src/util/logs-polling-utilities.ts +++ b/packages/contentstack-launch/src/util/logs-polling-utilities.ts @@ -1,13 +1,9 @@ -import EventEmitter from "events"; -import { ux } from "@contentstack/cli-utilities"; -import { ApolloClient, ObservableQuery } from "@apollo/client/core"; +import EventEmitter from 'events'; +import { ux } from '@contentstack/cli-utilities'; +import { ApolloClient, ObservableQuery } from '@apollo/client/core'; -import { LogPollingInput, ConfigType } from "../types"; -import { - deploymentQuery, - deploymentLogsQuery, - serverlessLogsQuery, -} from "../graphql"; +import { LogPollingInput, ConfigType } from '../types'; +import { deploymentQuery, deploymentLogsQuery, serverlessLogsQuery } from '../graphql'; export default class LogPolling { private config: ConfigType; @@ -69,15 +65,15 @@ export default class LogPolling { let statusWatchQuery = this.getDeploymentStatus(); statusWatchQuery.subscribe(({ data, errors, error }) => { if (error) { - this.$event.emit("deployment-logs", { + this.$event.emit('deployment-logs', { message: error?.message, - msgType: "error", + msgType: 'error', }); } if (errors?.length && data === null) { - this.$event.emit("deployment-logs", { + this.$event.emit('deployment-logs', { message: errors, - msgType: "error", + msgType: 'error', }); statusWatchQuery.stopPolling(); } @@ -110,33 +106,33 @@ export default class LogPolling { { deploymentUid: string | undefined; } - > + >, ): void { let timestamp: number = 0; logsWatchQuery.subscribe(({ data, errors, error }) => { - ux.action.start("Loading deployment logs..."); + ux.action.start('Loading deployment logs...'); if (error) { ux.action.stop(); - this.$event.emit("deployment-logs", { + this.$event.emit('deployment-logs', { message: error?.message, - msgType: "error", + msgType: 'error', }); - this.$event.emit("deployment-logs", { - message: "DONE", - msgType: "debug", + this.$event.emit('deployment-logs', { + message: 'DONE', + msgType: 'debug', }); logsWatchQuery.stopPolling(); } if (errors?.length && data === null) { ux.action.stop(); - this.$event.emit("deployment-logs", { + this.$event.emit('deployment-logs', { message: errors, - msgType: "error", + msgType: 'error', }); - this.$event.emit("deployment-logs", { - message: "DONE", - msgType: "debug", + this.$event.emit('deployment-logs', { + message: 'DONE', + msgType: 'debug', }); logsWatchQuery.stopPolling(); } @@ -144,12 +140,11 @@ export default class LogPolling { let logsData = data?.getLogs; if (logsData?.length) { ux.action.stop(); - this.$event.emit("deployment-logs", { + this.$event.emit('deployment-logs', { message: logsData, - msgType: "info", + msgType: 'info', }); - timestamp = - new Date(logsData[logsData.length - 1].timestamp).getTime() + 1; + timestamp = new Date(logsData[logsData.length - 1].timestamp).getTime() + 1; logsWatchQuery.setVariables({ deploymentUid: this.config.deployment, timestamp: new Date(timestamp).toISOString(), @@ -158,9 +153,9 @@ export default class LogPolling { if (this.config.deploymentStatus.includes(this.deploymentStatus)) { logsWatchQuery.stopPolling(); - this.$event.emit("deployment-logs", { - message: "DONE", - msgType: "debug", + this.$event.emit('deployment-logs', { + message: 'DONE', + msgType: 'debug', }); } } @@ -208,17 +203,17 @@ export default class LogPolling { endTime: number; }; } - > + >, ): void { serverLogsWatchQuery.subscribe(({ data, errors, error }) => { - ux.action.start("Loading server logs..."); + ux.action.start('Loading server logs...'); if (error) { ux.action.stop(); - this.$event.emit("server-logs", { message: error?.message, msgType: "error" }); + this.$event.emit('server-logs', { message: error?.message, msgType: 'error' }); } if (errors?.length && data === null) { ux.action.stop(); - this.$event.emit("server-logs", { message: errors, msgType: "error" }); + this.$event.emit('server-logs', { message: errors, msgType: 'error' }); serverLogsWatchQuery.stopPolling(); } @@ -226,9 +221,8 @@ export default class LogPolling { let logsLength = logsData?.length; if (logsLength > 0) { ux.action.stop(); - this.$event.emit("server-logs", { message: logsData, msgType: "info" }); - this.startTime = - new Date(logsData[logsLength - 1].timestamp).getTime() + 1; + this.$event.emit('server-logs', { message: logsData, msgType: 'info' }); + this.startTime = new Date(logsData[logsLength - 1].timestamp).getTime() + 1; this.endTime = this.startTime + 10 * 1000; } else if (logsLength === 0) { this.endTime = this.endTime + 10 * 1000; @@ -243,4 +237,3 @@ export default class LogPolling { }); } } - diff --git a/packages/contentstack/README.md b/packages/contentstack/README.md index 93207d43ac..b9bf9f6e5a 100644 --- a/packages/contentstack/README.md +++ b/packages/contentstack/README.md @@ -18,7 +18,7 @@ $ npm install -g @contentstack/cli $ csdx COMMAND running command... $ csdx (--version|-v) -@contentstack/cli/1.22.0 darwin-arm64 node-v22.2.0 +@contentstack/cli/1.23.0 darwin-arm64 node-v22.2.0 $ csdx --help [COMMAND] USAGE $ csdx COMMAND diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index b62d2f0f3e..ee5f537e43 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli", "description": "Command-line tool (CLI) to interact with Contentstack", - "version": "1.22.0", + "version": "1.23.0", "author": "Contentstack", "bin": { "csdx": "./bin/run.js" @@ -35,7 +35,7 @@ "@contentstack/cli-cm-seed": "~1.7.8", "@contentstack/cli-command": "~1.2.19", "@contentstack/cli-config": "~1.6.5", - "@contentstack/cli-launch": "~1.1.0", + "@contentstack/cli-launch": "~1.2.0", "@contentstack/cli-migration": "~1.6.1", "@contentstack/cli-utilities": "~1.7.1", "@contentstack/management": "~1.17.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac2126ef97..41817b8745 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ importers: '@contentstack/cli-cm-seed': ~1.7.8 '@contentstack/cli-command': ~1.2.19 '@contentstack/cli-config': ~1.6.5 - '@contentstack/cli-launch': ~1.1.0 + '@contentstack/cli-launch': ~1.2.0 '@contentstack/cli-migration': ~1.6.1 '@contentstack/cli-utilities': ~1.7.1 '@contentstack/management': ~1.17.0