From 712cecf38d23b245a788a3fe370f988bc034de3c Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Mon, 24 Nov 2025 15:52:59 +0100 Subject: [PATCH] feat: allow 'instanceInfo' to be added to the version checker hook (#11018) Allows the `checkLatestVersion` function in the `VersionService` to accept an optional `instanceInfo` parameter. If provided, and if the promise returns a value that is truthy, then it will add `instanceInfo` to the versionPayload. The license key may not contain a plan or a customer name, and while it definitely won't contain a client id, it has been requested that we report `self-hosted` as the client ID (will be handled in enterprise). Adding a second, optional parameter seemed to be the most backwards compatible way of doing this rather than changing the established method / callback types. --- src/lib/services/version-service.test.ts | 63 +++++++++++++++++++++++- src/lib/services/version-service.ts | 11 +++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/lib/services/version-service.test.ts b/src/lib/services/version-service.test.ts index 8f96e15d4f..daf4336f59 100644 --- a/src/lib/services/version-service.test.ts +++ b/src/lib/services/version-service.test.ts @@ -2,7 +2,7 @@ import nock from 'nock'; import createStores from '../../test/fixtures/store.js'; import version from '../util/version.js'; import getLogger from '../../test/fixtures/no-logger.js'; -import VersionService from './version-service.js'; +import VersionService, { type IInstanceInfo } from './version-service.js'; import { randomId } from '../util/random-id.js'; beforeAll(() => { @@ -347,3 +347,64 @@ test('Counts production changes', async () => { expect(scope.isDone()).toEqual(true); nock.cleanAll(); }); + +describe('instance info reading', () => { + test('it sets instance info if the instanceInfoProvider promise returns a truthy value', async () => { + const instanceInfo: IInstanceInfo = { + customerPlan: 'Test Plan', + customerName: 'Test Company', + clientId: 'Test Id', + }; + + const url = `https://${randomId()}.example.com`; + const scope = nock(url) + .post( + '/', + (body) => + body.instanceInfo && + body.instanceInfo.customerPlan === + instanceInfo.customerPlan && + body.instanceInfo.customerName === + instanceInfo.customerName && + body.instanceInfo.clientId === instanceInfo.clientId, + ) + .reply(() => [200]); + + const stores = createStores(); + const service = new VersionService(stores, { + getLogger, + versionCheck: { url, enable: true }, + telemetry: true, + }); + await service.checkLatestVersion( + () => Promise.resolve(fakeTelemetryData), + () => Promise.resolve(instanceInfo), + ); + expect(scope.isDone()).toEqual(true); + }); + + test.each([ + ['is undefined', undefined], + ['returns undefined', () => Promise.resolve(undefined)], + ])( + 'it does not set instance info if the instanceInfoProvider promise %s', + async (_, instanceInfoProvider) => { + const url = `https://${randomId()}.example.com`; + const scope = nock(url) + .post('/', (body) => body.instanceInfo === undefined) + .reply(() => [200]); + + const stores = createStores(); + const service = new VersionService(stores, { + getLogger, + versionCheck: { url, enable: true }, + telemetry: true, + }); + await service.checkLatestVersion( + () => Promise.resolve(fakeTelemetryData), + instanceInfoProvider, + ); + expect(scope.isDone()).toEqual(true); + }, + ); +}); diff --git a/src/lib/services/version-service.ts b/src/lib/services/version-service.ts index 639d876018..51baaf7009 100644 --- a/src/lib/services/version-service.ts +++ b/src/lib/services/version-service.ts @@ -58,6 +58,12 @@ export interface IFeatureUsageInfo { edgeInstanceUsage?: EdgeInstanceUsage; } +export type IInstanceInfo = Partial<{ + customerPlan: string; + customerName: string; + clientId: string; +}>; + export default class VersionService { private logger: Logger; @@ -131,6 +137,7 @@ export default class VersionService { async checkLatestVersion( telemetryDataProvider: () => Promise, + instanceInfoProvider?: () => Promise, ): Promise { const instanceId = await this.getInstanceId(); this.logger.debug( @@ -145,6 +152,10 @@ export default class VersionService { if (this.telemetryEnabled) { versionPayload.featureInfo = await telemetryDataProvider(); + const instanceInfo = await instanceInfoProvider?.(); + if (instanceInfo) { + versionPayload.instanceInfo = instanceInfo; + } } if (this.versionCheckUrl) { const res = await ky.post(this.versionCheckUrl, {