diff --git a/src/base-command.ts b/src/base-command.ts index 4664229..b6ff322 100755 --- a/src/base-command.ts +++ b/src/base-command.ts @@ -19,7 +19,7 @@ import { import config from './config'; import { GraphqlApiClient, Logger } from './util'; -import { getLaunchHubUrl } from './util/common-utility'; +import { getLaunchHubUrl, getAnalyticsInfo } from './util/common-utility'; import { ConfigType, LogFn, Providers, GraphqlHeaders } from './types'; export type Flags = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>; @@ -202,8 +202,8 @@ export abstract class BaseCommand extends Command { */ async prepareApiClients(): Promise { let headers: GraphqlHeaders = { - 'X-CS-CLI': this.context.analyticsInfo - } + 'X-CS-CLI': getAnalyticsInfo(this.context, this.config), + }; const { uid, organizationUid } = this.sharedConfig.currentConfig; @@ -232,7 +232,7 @@ export abstract class BaseCommand extends Command { * @memberof BaseCommand */ async initCmaSDK() { - managementSDKInitiator.init(this.context); + managementSDKInitiator.init({ ...this.context, analyticsInfo: getAnalyticsInfo(this.context, this.config) }); this.managementSdk = await managementSDKClient({ host: this.sharedConfig.host, }); diff --git a/src/commands/launch/index.ts b/src/commands/launch/index.ts index 72f5f0e..c607667 100755 --- a/src/commands/launch/index.ts +++ b/src/commands/launch/index.ts @@ -4,6 +4,7 @@ import config from '../../config'; import { BaseCommand } from '../../base-command'; import { AdapterConstructorInputs, Providers } from '../../types'; import { FileUpload, GitHub, PreCheck } from '../../adapters'; +import { getAnalyticsInfo } from '../../util/common-utility'; import { FlagInput, Flags, cliux } from '@contentstack/cli-utilities'; export default class Launch extends BaseCommand { @@ -137,7 +138,7 @@ export default class Launch extends BaseCommand { config: this.sharedConfig, apolloClient: this.apolloClient, managementSdk: this.managementSdk, - analyticsInfo: this.context.analyticsInfo, + analyticsInfo: getAnalyticsInfo(this.context, this.config), }; switch (this.sharedConfig.provider) { @@ -167,7 +168,7 @@ export default class Launch extends BaseCommand { config: this.sharedConfig, apolloClient: this.apolloClient, managementSdk: this.managementSdk, - analyticsInfo: this.context.analyticsInfo, + analyticsInfo: getAnalyticsInfo(this.context, this.config), }); await this.preCheck.run(); diff --git a/src/util/common-utility.ts b/src/util/common-utility.ts index 71cfc43..610f947 100644 --- a/src/util/common-utility.ts +++ b/src/util/common-utility.ts @@ -133,6 +133,27 @@ async function selectProject(options: { } } +/** + * Resolve the `X-CS-CLI` header value. oclif reloads the Config after the init hook, so + * `context.analyticsInfo` is often undefined at command runtime; reconstruct it from the + * Config and the persisted config store (clientId/sessionId) when that happens. + */ +function getAnalyticsInfo(context?: Record, config?: Record): string { + const fromContext = context?.analyticsInfo; + if (typeof fromContext === 'string' && fromContext.length > 0) { + return fromContext; + } + + const platform = config?.platform && config?.arch ? `${config.platform}-${config.arch}` : 'none'; + const nodeVersion = process.versions.node ? `v${process.versions.node}` : process.version; + const cliVersion = config?.version || 'none'; + const clientId = configHandler.get('clientId') || 'none'; + const sessionId = configHandler.get('sessionId') || 'none'; + const command = configHandler.get('currentCommandId') || 'launch'; + + return [platform, nodeVersion, cliVersion, clientId, sessionId, command].join(';'); +} + function getLaunchHubUrl(): string { const { cma } = configHandler.get('region') || {}; if (!cma) { @@ -151,4 +172,4 @@ function getLaunchHubUrl(): string { return `https://${launchHubBaseUrl}`; } -export { getOrganizations, selectOrg, selectProject, getLaunchHubUrl }; +export { getOrganizations, selectOrg, selectProject, getLaunchHubUrl, getAnalyticsInfo }; diff --git a/test/unit/util/common-utility.test.ts b/test/unit/util/common-utility.test.ts new file mode 100644 index 00000000..37f2aaa --- /dev/null +++ b/test/unit/util/common-utility.test.ts @@ -0,0 +1,64 @@ +import { describe, it, beforeEach, afterEach } from 'mocha'; +import { expect } from 'chai'; +import { createSandbox, SinonSandbox } from 'sinon'; +import { configHandler } from '@contentstack/cli-utilities'; + +import { getAnalyticsInfo } from '../../../src/util/common-utility'; + +describe('getAnalyticsInfo', () => { + let sandbox: SinonSandbox; + + beforeEach(() => { + sandbox = createSandbox(); + const getStub = sandbox.stub(configHandler, 'get'); + getStub.withArgs('clientId').returns('client-123'); + getStub.withArgs('sessionId').returns('session-456'); + getStub.withArgs('currentCommandId').returns('launch:deployments'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('returns the canonical context.analyticsInfo when it is a non-empty string', () => { + const result = getAnalyticsInfo( + { analyticsInfo: 'darwin-arm64;v22.0.0;2.0.0;abc;def;launch' }, + { platform: 'linux', arch: 'x64', version: '9.9.9' }, + ); + + expect(result).to.equal('darwin-arm64;v22.0.0;2.0.0;abc;def;launch'); + }); + + it('reconstructs the value from config and config store when context has no analyticsInfo', () => { + const result = getAnalyticsInfo({}, { platform: 'darwin', arch: 'arm64', version: '2.0.0-beta.15' }); + + expect(result).to.equal( + `darwin-arm64;v${process.versions.node};2.0.0-beta.15;client-123;session-456;launch:deployments`, + ); + }); + + it('falls back to reconstruction when context.analyticsInfo is an empty string', () => { + const result = getAnalyticsInfo({ analyticsInfo: '' }, { platform: 'darwin', arch: 'arm64', version: '2.0.0' }); + + expect(result).to.equal(`darwin-arm64;v${process.versions.node};2.0.0;client-123;session-456;launch:deployments`); + }); + + it('uses safe defaults when config is missing but still reads the config store', () => { + const result = getAnalyticsInfo(undefined, undefined); + + expect(result).to.equal(`none;v${process.versions.node};none;client-123;session-456;launch:deployments`); + }); + + it('defaults the command segment to "launch" when currentCommandId is not set', () => { + sandbox.restore(); + sandbox = createSandbox(); + const getStub = sandbox.stub(configHandler, 'get'); + getStub.withArgs('clientId').returns('client-123'); + getStub.withArgs('sessionId').returns('session-456'); + getStub.withArgs('currentCommandId').returns(undefined); + + const result = getAnalyticsInfo({}, { platform: 'darwin', arch: 'arm64', version: '2.0.0' }); + + expect(result).to.equal(`darwin-arm64;v${process.versions.node};2.0.0;client-123;session-456;launch`); + }); +});