pcf-scripts
Version:
This package contains a module for building PowerApps Component Framework (PCF) controls. See project homepage how to install.
116 lines (114 loc) • 5.89 kB
JavaScript
;
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TelemetryUserSettingsFileProvider = void 0;
const fs = require("fs-extra");
const path = require("node:path");
const uuid_1 = require("uuid");
const SettingVersionId = "1.0";
// The implementation of this class uses the same methods as implemented in the CRM.DevToolsCore repo to keep the reliability consistent.
// See: https://dev.azure.com/dynamicscrm/OneCRM/_git/CRM.DevToolsCore?path=src/GeneralTools/BatchedTelemetry/BatchedTelemetry/Extensibility/TelemetryUserSettingsFileProvider.cs&version=GBmaster&_a=contents
class TelemetryUserSettingsFileProvider {
constructor(settingsFilePath) {
if (!settingsFilePath)
throw new Error("settingsFilePath is required.");
// Note: We resolve the path to a full path to ensure that changes to Environment.CurrentDirectory won't cause unexpected, hard to trace IO exceptions.
this.settingsFilePath = path.resolve(settingsFilePath);
}
GetCurrent() {
try {
// Always load fresh from persisted file
const settings = this.SafeLoadSettings();
return {
uniqueId: settings.uniqueId,
telemetryEnabled: settings.telemetryEnabled,
};
}
catch (e) {
if (e instanceof Error) {
if (e.message.startsWith("UserSettingsErrorReason.")) {
throw e;
}
else {
throw new Error(`UserSettingsErrorReason.Unspecified - ${e.message}`);
}
}
throw new Error(`UserSettingsErrorReason.Unspecified`);
}
}
SafeLoadSettings() {
this.EnsureSettingsFileExists();
const settings = this.ReadSettings();
// Some old files may not have the UniqueId set
if (!settings.uniqueId || settings.uniqueId === "00000000-0000-0000-0000-000000000000") {
// BatchedTelemetryTraceSource.TraceInformation($"{nameof(TelemetryUserSettingsFileProvider)} User settings file missing unique id. Generating and saving new id. Settings file path: {SettingsFilePath}.");
settings.uniqueId = (0, uuid_1.v4)();
this.WriteSettings(settings, /*allowOverwrite*/ true);
}
// uniqueId is now guaranteed
return settings;
}
EnsureSettingsFileExists() {
if (fs.existsSync(this.settingsFilePath)) {
return;
}
// Ensure the folder path exists
fs.ensureDirSync(path.dirname(this.settingsFilePath));
// otherwise, we need to create the file, as long as no other process is also creating it
const proposedUserSettings = {
settingVersion: SettingVersionId,
uniqueId: (0, uuid_1.v4)(),
telemetryEnabled: true,
};
try {
this.WriteSettings(proposedUserSettings, /*allowOverwrite*/ false);
}
catch {
// This indicates that between our call to File.Exists and WriteSettings that another process was able to successfully write the settings file.
// In this case, we ignore the exception as now the exist condition for this function is satisfied.
}
}
ReadSettings() {
// Note: This function assumes the file already exists.
// TODO: use MultiProcessIO.FileReadAllTextWithRetries (in CRM.DevToolsCore) if we get errors from customers regarding multi-process write errors
const json = fs.readFileSync(this.settingsFilePath, "utf8");
try {
// Parse json and deserialize, with data contract validation
const parsed = JSON.parse(json);
if (parsed.settingVersion !== SettingVersionId)
throw new Error(`settingVersion should be '${SettingVersionId}'.`);
if (parsed.uniqueId && parsed.uniqueId.length !== 36)
throw new Error(`uniqueId is allowed to be undefined or it should be a Guid of length 36.`);
if (typeof parsed.telemetryEnabled !== "boolean")
throw new Error(`telemetryEnabled should be a boolean.`);
return parsed;
}
catch {
// Then the settings file is invalid or corrupted
// This shouldn't happen unless there was a catastrophic disk error, invalid serialization logic, or user error.
// In any of these cases, this indicates a bug in our code that we should fix or the user should be instructed to fix their installation.
throw new Error("UserSettingsErrorReason.InvalidFileContents");
}
}
WriteSettings(proposedUserSettings, allowOverwrite) {
// Serialize the settings to json
if (proposedUserSettings.settingVersion !== SettingVersionId)
throw new Error(`settingVersion should be '${SettingVersionId}'.`);
if (!proposedUserSettings.uniqueId || proposedUserSettings.uniqueId.length !== 36)
throw new Error(`uniqueId should be a Guid of length 36.`);
if (typeof proposedUserSettings.telemetryEnabled !== "boolean")
throw new Error(`telemetryEnabled should be a boolean.`);
const json = JSON.stringify(proposedUserSettings);
// see: https://nodejs.org/api/fs.html#file-system-flags
const fsFlag = allowOverwrite ? "w" : "wx";
// TODO: use MultiProcessIO.CreateExclusiveFileStreamWithRetries (in CRM.DevToolsCore) if we get errors from customers regarding multi-process write errors
fs.writeFileSync(this.settingsFilePath, json, {
encoding: "utf8",
flag: fsFlag,
});
}
}
exports.TelemetryUserSettingsFileProvider = TelemetryUserSettingsFileProvider;
//# sourceMappingURL=TelemetryUserSettingsFileProvider.js.map