generated from SAP/repository-template
-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fiori-gen): add telemetry helper class and associated functions (#…
…2489) * feat(fiori-gen): add common utilities * feat(fiori-gen): changeset * feat(fiori-gen): fix test * feat(fiori-gen): lint * feat(fiori-gen): update odata service inq * feat(fiori-gen): update gethostenvironment function * feat(fiori-gen): fix test * feat(ref-lib): revert odata serivce inq changes * feat(ref-lib): revert odata serivce inq changes * feat(ref-lib): lint & export * Linting auto fix commit * feat(fiori-gen): update param name --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: IainSAP <[email protected]>
- Loading branch information
1 parent
bb4d1e2
commit 231e713
Showing
17 changed files
with
474 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@sap-ux/fiori-generator-shared': minor | ||
--- | ||
|
||
adds new functions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
export * from './cap'; | ||
export * from './environment'; | ||
export * from './logWrapper'; | ||
export * from './system-utils'; | ||
export * from './telemetry'; | ||
export { getPackageScripts } from './getPackageScripts'; | ||
export { getBootstrapResourceUrls, getDefaultTargetFolder } from './helpers'; | ||
export { generateReadMe } from './read-me'; | ||
export * from './system-utils'; | ||
export { PackageJsonScripts, YeomanEnvironment, VSCodeInstance } from './types'; | ||
export * from './logWrapper'; | ||
export { getHostEnvironment } from './environment'; | ||
export { isExtensionInstalled } from './installedCheck'; | ||
export { PackageJsonScripts, YeomanEnvironment, VSCodeInstance, hostEnvironment } from './types'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { coerce, lt } from 'semver'; | ||
|
||
/** | ||
* Check for an installed extension, optionally specifying a minimum version. | ||
* Note, this does not check for activation state of specified extension. | ||
* | ||
* @param vscode - vscode instance | ||
* @param extensionId - the id of the extension to find | ||
* @param minVersion - the minimum version of the specified extension, lower versions will not be returned. Must be a valid SemVer string. | ||
* @returns true if the extension is installed and the version is >= minVersion (if provided), false otherwise | ||
*/ | ||
export function isExtensionInstalled(vscode: any, extensionId: string, minVersion?: string): boolean { | ||
const foundExt = vscode?.extensions?.getExtension(extensionId); | ||
if (foundExt) { | ||
const extVersion = coerce(foundExt.packageJSON.version); | ||
if (extVersion) { | ||
// Check installed ver is >= minVersion or return true if minVersion is not specified | ||
return !(minVersion && lt(extVersion, minVersion)); | ||
} | ||
} | ||
return false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { sendTelemetry, sendTelemetryBlocking } from './utils'; | ||
export * from './telemetryHelper'; |
103 changes: 103 additions & 0 deletions
103
packages/fiori-generator-shared/src/telemetry/telemetryHelper.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { | ||
PerformanceMeasurementAPI as Performance, | ||
initTelemetrySettings, | ||
type TelemetryProperties, | ||
type ToolsSuiteTelemetryInitSettings | ||
} from '@sap-ux/telemetry'; | ||
import type { TelemetryData } from './types'; | ||
import { getHostEnvironment } from '../environment'; | ||
import osName from 'os-name'; | ||
import { t } from '../i18n'; | ||
|
||
/** | ||
* Helper class for intialising and preparing event data for telemetry. | ||
*/ | ||
export abstract class TelemetryHelper { | ||
private static _telemetryData: TelemetryData; | ||
private static _previousEventTimestamp: number; | ||
|
||
/** | ||
* Returns the telemetry data. | ||
* | ||
* @returns telemetry data | ||
*/ | ||
public static get telemetryData(): TelemetryData { | ||
return this._telemetryData; | ||
} | ||
|
||
/** | ||
* Load telemetry settings. | ||
* | ||
* @param options - tools suite telemetry init settings | ||
*/ | ||
public static async initTelemetrySettings(options: ToolsSuiteTelemetryInitSettings): Promise<void> { | ||
await initTelemetrySettings(options); | ||
} | ||
|
||
/** | ||
* Creates telemetry data and adds default telemetry props. | ||
* | ||
* @param additionalData - set additional properties to be reported by telemetry | ||
* @param filterDups - filters duplicates by returning undefined if it's suspected to be a repeated event based on previous telemetry data & timestamp (1 second) | ||
* @returns telemetry data | ||
*/ | ||
public static createTelemetryData<T extends TelemetryProperties>( | ||
additionalData?: Partial<T>, | ||
filterDups = false | ||
): TelemetryData | undefined { | ||
const currentTimestamp = new Date().getTime(); | ||
if (!this._previousEventTimestamp) { | ||
filterDups = false; // can't filter duplicates if no previous event timestamp | ||
this._previousEventTimestamp = currentTimestamp; | ||
} | ||
|
||
if (!this._telemetryData) { | ||
let osVersionName = t('telemetry.unknownOs'); | ||
try { | ||
osVersionName = osName(); | ||
} catch { | ||
// no matched os name, possible beta or unreleased version | ||
} | ||
this._telemetryData = { | ||
Platform: getHostEnvironment().technical, | ||
OperatingSystem: osVersionName | ||
}; | ||
} | ||
|
||
if (filterDups) { | ||
const newTelemData = { ...this._telemetryData, ...additionalData }; | ||
if ( | ||
Math.abs(this._previousEventTimestamp - currentTimestamp) < 1000 && | ||
JSON.stringify(newTelemData) === JSON.stringify(this._telemetryData) | ||
) { | ||
return undefined; | ||
} | ||
} | ||
this._previousEventTimestamp = currentTimestamp; | ||
this._telemetryData = Object.assign(this._telemetryData, additionalData); | ||
|
||
return this._telemetryData; | ||
} | ||
|
||
/** | ||
* Marks the start time. Example usage: | ||
* At the start of of the writing phase of the yeoman generator. | ||
* It should not be updated everytime calling createTelemetryData(). | ||
*/ | ||
public static markAppGenStartTime(): void { | ||
TelemetryHelper.createTelemetryData({ | ||
markName: Performance.startMark('LOADING_TIME') | ||
}); | ||
} | ||
|
||
/** | ||
* Marks the end time. Example usage: | ||
* At the end of the writing phase of yeoman generator. | ||
*/ | ||
public static markAppGenEndTime(): void { | ||
if (this._telemetryData?.markName) { | ||
Performance.endMark(this._telemetryData.markName); | ||
Performance.measure(this._telemetryData.markName); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export interface TelemetryData { | ||
[key: string]: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { ClientFactory, PerformanceMeasurementAPI as Performance, SampleRate } from '@sap-ux/telemetry'; | ||
import type { TelemetryEvent } from '@sap-ux/telemetry'; | ||
import type { TelemetryData } from './types'; | ||
import { TelemetryHelper } from './telemetryHelper'; | ||
|
||
/** | ||
* Prepares the telemetry event to be sent to the telemetry client. | ||
* | ||
* @param telemetryEventName - the event name to be reported | ||
* @param telemetryData - the telemetry data | ||
* @returns - the telemetry event | ||
*/ | ||
function prepareTelemetryEvent(telemetryEventName: string, telemetryData: TelemetryData): TelemetryEvent { | ||
// Make sure performance measurement end is called | ||
TelemetryHelper.markAppGenEndTime(); | ||
const generationTime = telemetryData.markName | ||
? Performance.getMeasurementDuration(telemetryData.markName) | ||
: undefined; | ||
|
||
return { | ||
eventName: telemetryEventName, | ||
properties: telemetryData, | ||
measurements: generationTime ? { GenerationTime: generationTime } : {} | ||
}; | ||
} | ||
|
||
/** | ||
* Sends the telemetry event to the telemetry client. | ||
* | ||
* @param telemetryEventName - the event name to be reported | ||
* @param telemetryData - the telemetry data | ||
* @param appPath - the path of the application | ||
* @returns - a promise that resolves when the event is sent | ||
*/ | ||
export async function sendTelemetry( | ||
telemetryEventName: string, | ||
telemetryData: TelemetryData, | ||
appPath?: string | ||
): Promise<void> { | ||
const telemetryEvent = prepareTelemetryEvent(telemetryEventName, telemetryData); | ||
return ClientFactory.getTelemetryClient().reportEvent( | ||
telemetryEvent, | ||
SampleRate.NoSampling, | ||
appPath ? { appPath } : undefined | ||
); | ||
} | ||
|
||
/** | ||
* Sends the telemetry event to the telemetry client and blocks the execution until the event is sent. | ||
* | ||
* @param telemetryEventName - the event name to be reported | ||
* @param telemetryData - the telemetry data | ||
* @param appPath - the path of the application | ||
* @returns - a promise that resolves when the event is sent | ||
*/ | ||
export async function sendTelemetryBlocking( | ||
telemetryEventName: string, | ||
telemetryData: TelemetryData, | ||
appPath?: string | ||
): Promise<void> { | ||
const telemetryEvent = prepareTelemetryEvent(telemetryEventName, telemetryData); | ||
return ClientFactory.getTelemetryClient().reportEventBlocking( | ||
telemetryEvent, | ||
SampleRate.NoSampling, | ||
appPath ? { appPath } : undefined | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { hostEnvironment } from '../src/types'; | ||
import { isCli, getHostEnvironment } from '../src/environment'; | ||
import * as btpUtils from '@sap-ux/btp-utils'; | ||
|
||
jest.mock('@sap-ux/btp-utils', () => { | ||
return { | ||
__esModule: true, | ||
...jest.requireActual('@sap-ux/btp-utils') | ||
}; | ||
}); | ||
|
||
function mockCli(isCli: boolean) { | ||
process.argv[1] = isCli ? 'path/to/yo' : 'path/to/mock'; | ||
process.stdin.isTTY = isCli ? true : false; | ||
} | ||
|
||
describe('environment utils', () => { | ||
const originalArgv = process.argv; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
process.argv = [...originalArgv]; | ||
}); | ||
|
||
afterEach(() => { | ||
process.argv = originalArgv; | ||
}); | ||
|
||
afterAll(() => { | ||
process.stdin.destroy(); | ||
}); | ||
|
||
it('should return true for cli', () => { | ||
mockCli(true); | ||
expect(isCli()).toBe(true); | ||
}); | ||
|
||
it('should return false for non-cli', () => { | ||
mockCli(false); | ||
expect(isCli()).toBe(false); | ||
}); | ||
|
||
it('should return correct host environment - cli', () => { | ||
mockCli(true); | ||
expect(getHostEnvironment()).toEqual(hostEnvironment.cli); | ||
}); | ||
|
||
it('should return correct host environment - app studio', () => { | ||
mockCli(false); | ||
jest.spyOn(btpUtils, 'isAppStudio').mockReturnValueOnce(true); | ||
expect(getHostEnvironment()).toEqual(hostEnvironment.bas); | ||
}); | ||
|
||
it('should return correct host environment - vscode', () => { | ||
mockCli(false); | ||
jest.spyOn(btpUtils, 'isAppStudio').mockReturnValueOnce(false); | ||
expect(getHostEnvironment()).toEqual(hostEnvironment.vscode); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
packages/fiori-generator-shared/test/installedCheck.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { isExtensionInstalled } from '../src/installedCheck'; | ||
|
||
describe('Installed module checker', () => { | ||
test('isExtensionInstalled', () => { | ||
expect(isExtensionInstalled(undefined, 'wont.be.found.extension')).toBe(false); | ||
|
||
const mockVSCodeRef = { | ||
extensions: { | ||
getExtension: () => ({ | ||
packageJSON: { | ||
version: '1.2.3' | ||
} | ||
}) | ||
} | ||
}; | ||
|
||
expect(isExtensionInstalled(mockVSCodeRef, 'will.be.found.extension')).toBe(true); | ||
expect(isExtensionInstalled(mockVSCodeRef, 'version.not.satisfied.extension', '1.2.4')).toBe(false); | ||
expect(isExtensionInstalled(mockVSCodeRef, 'version.equal.extension', '1.2.3')).toBe(true); | ||
expect(isExtensionInstalled(mockVSCodeRef, 'version.lower.extension', '0.0.1')).toBe(true); | ||
}); | ||
}); |
Oops, something went wrong.