Added support for self serve telemetry + Localization fixes (#580)
* initial telemetry commit * Added localization changes * moved telemetrymessage types to selfservetypes * fixed compile errors * fixed failing test * changed translation file format * Addressed PR comments * modified test
This commit is contained in:
parent
63e13cdabe
commit
6cdac3c53b
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { DescriptionType, NumberUiType, SmartUiInput } from "../../../SelfServe/SelfServeTypes";
|
||||
import { SmartUiComponent, SmartUiDescriptor } from "./SmartUiComponent";
|
||||
import { NumberUiType, SmartUiInput, DescriptionType } from "../../../SelfServe/SelfServeTypes";
|
||||
|
||||
describe("SmartUiComponent", () => {
|
||||
const exampleData: SmartUiDescriptor = {
|
||||
|
@ -97,9 +97,9 @@ describe("SmartUiComponent", () => {
|
|||
dataFieldName: "database",
|
||||
type: "object",
|
||||
choices: [
|
||||
{ label: "Database 1", key: "db1" },
|
||||
{ label: "Database 2", key: "db2" },
|
||||
{ label: "Database 3", key: "db3" },
|
||||
{ labelTKey: "Database 1", key: "db1" },
|
||||
{ labelTKey: "Database 2", key: "db2" },
|
||||
{ labelTKey: "Database 3", key: "db3" },
|
||||
],
|
||||
defaultKey: "db2",
|
||||
},
|
||||
|
|
|
@ -334,7 +334,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||
dropdownWidth="auto"
|
||||
options={choices.map((c) => ({
|
||||
key: c.key,
|
||||
text: this.props.getTranslation(c.label),
|
||||
text: this.props.getTranslation(c.labelTKey),
|
||||
}))}
|
||||
styles={{
|
||||
root: { width: 400 },
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"Save": "Save",
|
||||
"Discard": "Discard",
|
||||
"Refresh": "Refesh"
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"NorthCentralUS": "North Central US",
|
||||
"WestUS": "West US",
|
||||
"EastUS2": "East US 2",
|
||||
"Current Region": "Current Region",
|
||||
"RegionDropdownInfo": "More regions can be added in the future.",
|
||||
"RegionsAndAccountNameValidationError": "Regions and account name should not be empty.",
|
||||
"DbThroughputValidationError": "Please update throughput for database.",
|
||||
"DescriptionLabel": "Description",
|
||||
"DescriptionText": "This class sets collection and database throughput.",
|
||||
"DecriptionLinkText": "Click here for more information",
|
||||
"Regions": "Regions",
|
||||
"RegionsPlaceholder": "Select a region",
|
||||
"Enable Logging": "Enable Logging",
|
||||
"Enable": "Enable",
|
||||
"Disable": "Disable",
|
||||
"Account Name": "Account Name",
|
||||
"AccountNamePlaceHolder": "Enter the account name",
|
||||
"Collection Throughput": "Collection Throughput",
|
||||
"Enable DB level throughput": "Enable Database Level Throughput",
|
||||
"Database Throughput": "Database Throughput",
|
||||
"UpdateInProgressMessage": "Data is being updated",
|
||||
"UpdateCompletedMessageTitle": "Update succeeded",
|
||||
"UpdateCompletedMessageText": "Data update completed.",
|
||||
"SubmissionMessageSuccessTitle": "Update started",
|
||||
"SubmissionMessageForNewRegionText": "Data update started. Region changed.",
|
||||
"SubmissionMessageForSameRegionText": "Data update started. Region not changed.",
|
||||
"SubmissionMessageErrorTitle": "Data update failed",
|
||||
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
||||
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"DedicatedGatewayDescription": "Provision a dedicated gateway cluster for your Azure Cosmos DB account. A dedicated gateway is compute that is a front-end to data in your Azure Cosmos DB account. Your dedicated gateway automatically includes the integrated cache, which can improve read performance.",
|
||||
"DedicatedGateway": "Dedicated Gateway",
|
||||
"Enable": "Enable",
|
||||
"Disable": "Disable",
|
||||
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
|
||||
"DeprovisioningDetailsText": "Learn more about deprovisioning the dedicated gateway.",
|
||||
"DedicatedGatewayPricing": "Learn more about dedicated gateway pricing.",
|
||||
"SKUs": "SKUs",
|
||||
"SKUsPlaceHolder": "Select SKUs",
|
||||
"NumberOfInstances": "Number of instances",
|
||||
"CosmosD4s": "Cosmos.D4s (General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory)",
|
||||
"CosmosD8s": "Cosmos.D8s (General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory)",
|
||||
"CosmosD16s": "Cosmos.D16s (General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory)",
|
||||
"CosmosD32s": "Cosmos.D32s (General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory)",
|
||||
"CreateMessage": "Dedicated gateway resource is being created.",
|
||||
"CreateInitializeTitle": "Provisioning resource",
|
||||
"CreateInitializeMessage": "Dedicated gateway resource will be provisioned.",
|
||||
"CreateSuccessTitle": "Resource provisioned",
|
||||
"CreateSuccesseMessage": "Dedicated gateway resource provisioned.",
|
||||
"CreateFailureTitle": "Failed to provision resource",
|
||||
"CreateFailureMessage": "Dedicated gateway resource provisioning failed.",
|
||||
"UpdateMessage": "Dedicated gateway resource is being updated.",
|
||||
"UpdateInitializeTitle": "Updating resource",
|
||||
"UpdateInitializeMessage": "Dedicated gateway resource will be updated.",
|
||||
"UpdateSuccessTitle": "Resource updated",
|
||||
"UpdateSuccesseMessage": "Dedicated gateway resource updated.",
|
||||
"UpdateFailureTitle": "Failed to update resource",
|
||||
"UpdateFailureMessage": "Dedicated gateway resource updation failed.",
|
||||
"DeleteMessage": "Dedicated gateway resource is being deleted.",
|
||||
"DeleteInitializeTitle": "Deleting resource",
|
||||
"DeleteInitializeMessage": "Dedicated gateway resource will be deleted.",
|
||||
"DeleteSuccessTitle": "Resource deleted",
|
||||
"DeleteSuccesseMessage": "Dedicated gateway resource deleted.",
|
||||
"DeleteFailureTitle": "Failed to delete resource",
|
||||
"DeleteFailureMessage": "Dedicated gateway resource deletion failed.",
|
||||
"CannotSave": "Cannot save the changes to the Dedicated gateway resource at the moment.",
|
||||
"DedicatedGatewayEndpoint": "Dedicated gatewayEndpoint",
|
||||
"NoValue": "",
|
||||
"SKUDetails": "SKU Details:",
|
||||
"CosmosD4Details": "General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory",
|
||||
"CosmosD8Details": "General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory",
|
||||
"CosmosD16Details": "General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory",
|
||||
"CosmosD32Details": "General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory",
|
||||
"Cost": "Cost",
|
||||
"CostText": "Hourly cost of the dedicated gateway resource depends on the SKU selection, number of instances per region, and number of regions.",
|
||||
"ConnectionString": "Connection String",
|
||||
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
|
||||
"KeysBlade": "the keys blade",
|
||||
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",
|
||||
"WarningBannerOnDelete": "After deprovisioning the dedicated gateway, you must update any applications using the old dedicated gateway connection string."
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
{
|
||||
"translations": {
|
||||
"Common": {
|
||||
"Save": "Save",
|
||||
"Discard": "Discard",
|
||||
"Refresh": "Refesh"
|
||||
},
|
||||
"SelfServeExample": {
|
||||
"North Central US": "North Central US",
|
||||
"West US": "West US",
|
||||
"East US 2": "East US 2",
|
||||
"Current Region": "Current Region",
|
||||
"RegionDropdownInfo": "More regions can be added in the future.",
|
||||
"RegionsAndAccountNameValidationError": "Regions and account name should not be empty.",
|
||||
"DbThroughputValidationError": "Please update throughput for database.",
|
||||
"DescriptionLabel": "Description",
|
||||
"DescriptionText": "This class sets collection and database throughput.",
|
||||
"DecriptionLinkText": "Click here for more information",
|
||||
"Regions": "Regions",
|
||||
"RegionsPlaceholder": "Select a region",
|
||||
"Enable Logging": "Enable Logging",
|
||||
"Enable": "Enable",
|
||||
"Disable": "Disable",
|
||||
"Account Name": "Account Name",
|
||||
"AccountNamePlaceHolder": "Enter the account name",
|
||||
"Collection Throughput": "Collection Throughput",
|
||||
"Enable DB level throughput": "Enable Database Level Throughput",
|
||||
"Database Throughput": "Database Throughput",
|
||||
"UpdateInProgressMessage": "Data is being updated",
|
||||
"UpdateCompletedMessageTitle": "Update succeeded",
|
||||
"UpdateCompletedMessageText": "Data update completed.",
|
||||
"SubmissionMessageSuccessTitle": "Update started",
|
||||
"SubmissionMessageForNewRegionText": "Data update started. Region changed.",
|
||||
"SubmissionMessageForSameRegionText": "Data update started. Region not changed.",
|
||||
"SubmissionMessageErrorTitle": "Data update failed",
|
||||
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
||||
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
||||
},
|
||||
"SqlX": {
|
||||
"DedicatedGatewayDescription": "Provision a dedicated gateway cluster for your Azure Cosmos DB account. A dedicated gateway is compute that is a front-end to data in your Azure Cosmos DB account. Your dedicated gateway automatically includes the integrated cache, which can improve read performance. ",
|
||||
"DedicatedGateway": "Dedicated Gateway",
|
||||
"Enable": "Enable",
|
||||
"Disable": "Disable",
|
||||
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
|
||||
"DeprovisioningDetailsText": "Learn more about deprovisioning the dedicated gateway.",
|
||||
"DedicatedGatewayPricing": "Learn more about dedicated gateway pricing",
|
||||
"SKUs": "SKUs",
|
||||
"SKUsPlaceHolder": "Select SKUs",
|
||||
"NumberOfInstances": "Number of instances",
|
||||
"CosmosD4s": "Cosmos.D4s (General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory)",
|
||||
"CosmosD8s": "Cosmos.D8s (General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory)",
|
||||
"CosmosD16s": "Cosmos.D16s (General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory)",
|
||||
"CosmosD32s": "Cosmos.D32s (General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory)",
|
||||
"CreateMessage": "Dedicated gateway resource is being created.",
|
||||
"CreateInitializeTitle": "Provisioning resource",
|
||||
"CreateInitializeMessage": "Dedicated gateway resource will be provisioned.",
|
||||
"CreateSuccessTitle": "Resource provisioned",
|
||||
"CreateSuccesseMessage": "Dedicated gateway resource provisioned.",
|
||||
"CreateFailureTitle": "Failed to provision resource",
|
||||
"CreateFailureMessage": "Dedicated gateway resource provisioning failed.",
|
||||
"UpdateMessage": "Dedicated gateway resource is being updated.",
|
||||
"UpdateInitializeTitle": "Updating resource",
|
||||
"UpdateInitializeMessage": "Dedicated gateway resource will be updated.",
|
||||
"UpdateSuccessTitle": "Resource updated",
|
||||
"UpdateSuccesseMessage": "Dedicated gateway resource updated.",
|
||||
"UpdateFailureTitle": "Failed to update resource",
|
||||
"UpdateFailureMessage": "Dedicated gateway resource updation failed.",
|
||||
"DeleteMessage": "Dedicated gateway resource is being deleted.",
|
||||
"DeleteInitializeTitle": "Deleting resource",
|
||||
"DeleteInitializeMessage": "Dedicated gateway resource will be deleted.",
|
||||
"DeleteSuccessTitle": "Resource deleted",
|
||||
"DeleteSuccesseMessage": "Dedicated gateway resource deleted.",
|
||||
"DeleteFailureTitle": "Failed to delete resource",
|
||||
"DeleteFailureMessage": "Dedicated gateway resource deletion failed.",
|
||||
"CannotSave": "Cannot save the changes to the Dedicated gateway resource at the moment",
|
||||
"DedicatedGatewayEndpoint": "Dedicated gatewayEndpoint",
|
||||
"NoValue": "",
|
||||
"SKUDetails": "SKU Details: ",
|
||||
"CosmosD4Details": "General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory",
|
||||
"CosmosD8Details": "General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory",
|
||||
"CosmosD16Details": "General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory",
|
||||
"CosmosD32Details": "General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory",
|
||||
"Cost": "Cost",
|
||||
"CostText": "Hourly cost of the dedicated gateway resource depends on the SKU selection, number of instances per region, and number of regions.",
|
||||
"ConnectionString": "Connection String",
|
||||
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
|
||||
"KeysBlade": "the keys blade",
|
||||
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",
|
||||
"WarningBannerOnDelete": "After deprovisioning the dedicated gateway, you must update any applications using the old dedicated gateway connection string."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ import {
|
|||
} from "./SelfServeExample.rp";
|
||||
|
||||
const regionDropdownItems: ChoiceItem[] = [
|
||||
{ label: "North Central US", key: Regions.NorthCentralUS },
|
||||
{ label: "West US", key: Regions.WestUS },
|
||||
{ label: "East US 2", key: Regions.EastUS2 },
|
||||
{ labelTKey: "NorthCentralUS", key: Regions.NorthCentralUS },
|
||||
{ labelTKey: "WestUS", key: Regions.WestUS },
|
||||
{ labelTKey: "EastUS2", key: Regions.EastUS2 },
|
||||
];
|
||||
|
||||
const regionDropdownInfo: Info = {
|
||||
|
|
|
@ -2,10 +2,12 @@ import { Spinner, SpinnerSize } from "office-ui-fabric-react";
|
|||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
import * as React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
||||
import { sendReadyMessage } from "../Common/MessageHandler";
|
||||
import { configContext, updateConfigContext } from "../ConfigContext";
|
||||
import { SelfServeFrameInputs } from "../Contracts/ViewModels";
|
||||
import i18n from "../i18n";
|
||||
import { updateUserContext } from "../UserContext";
|
||||
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
||||
import "./SelfServe.less";
|
||||
|
@ -14,14 +16,35 @@ import { SelfServeDescriptor } from "./SelfServeTypes";
|
|||
import { SelfServeType } from "./SelfServeUtils";
|
||||
initializeIcons();
|
||||
|
||||
const loadTranslationFile = async (className: string): Promise<void> => {
|
||||
const language = i18n.languages[0];
|
||||
const fileName = `${className}.json`;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let translations: any;
|
||||
try {
|
||||
translations = await import(`../Localization/${language}/${fileName}`);
|
||||
} catch (e) {
|
||||
translations = await import(`../Localization/en/${fileName}`);
|
||||
}
|
||||
i18n.addResourceBundle(language, className, translations.default, true);
|
||||
};
|
||||
|
||||
const loadTranslations = async (className: string): Promise<void> => {
|
||||
await loadTranslationFile("Common");
|
||||
await loadTranslationFile(className);
|
||||
};
|
||||
|
||||
const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {
|
||||
switch (selfServeType) {
|
||||
case SelfServeType.example: {
|
||||
const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample");
|
||||
await loadTranslations(SelfServeExample.default.name);
|
||||
return new SelfServeExample.default().toSelfServeDescriptor();
|
||||
}
|
||||
case SelfServeType.sqlx: {
|
||||
const SqlX = await import(/* webpackChunkName: "SqlX" */ "./SqlX/SqlX");
|
||||
await loadTranslations(SqlX.default.name);
|
||||
return new SqlX.default().toSelfServeDescriptor();
|
||||
}
|
||||
default:
|
||||
|
@ -33,7 +56,8 @@ const renderComponent = (selfServeDescriptor: SelfServeDescriptor): JSX.Element
|
|||
if (!selfServeDescriptor) {
|
||||
return <h1>Invalid self serve type!</h1>;
|
||||
}
|
||||
return <SelfServeComponent descriptor={selfServeDescriptor} />;
|
||||
const SelfServeComponentTranslated = withTranslation()(SelfServeComponent);
|
||||
return <SelfServeComponentTranslated descriptor={selfServeDescriptor} />;
|
||||
};
|
||||
|
||||
const renderSpinner = (): JSX.Element => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { SelfServeComponent, SelfServeComponentState } from "./SelfServeComponent";
|
||||
import { NumberUiType, OnSaveResult, SelfServeDescriptor, SmartUiInput } from "./SelfServeTypes";
|
||||
|
||||
|
@ -87,9 +87,9 @@ describe("SelfServeComponent", () => {
|
|||
dataFieldName: "database",
|
||||
type: "object",
|
||||
choices: [
|
||||
{ label: "Database 1", key: "db1" },
|
||||
{ label: "Database 2", key: "db2" },
|
||||
{ label: "Database 3", key: "db3" },
|
||||
{ labelTKey: "Database 1", key: "db1" },
|
||||
{ labelTKey: "Database 2", key: "db2" },
|
||||
{ labelTKey: "Database 3", key: "db3" },
|
||||
],
|
||||
defaultKey: "db2",
|
||||
},
|
||||
|
@ -106,7 +106,9 @@ describe("SelfServeComponent", () => {
|
|||
};
|
||||
|
||||
it("should render and honor save, discard, refresh actions", async () => {
|
||||
const wrapper = shallow(<SelfServeComponent descriptor={exampleData} />);
|
||||
const wrapper = shallow(
|
||||
<SelfServeComponent descriptor={exampleData} t={undefined} i18n={undefined} tReady={undefined} />
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
|
@ -158,7 +160,9 @@ describe("SelfServeComponent", () => {
|
|||
});
|
||||
|
||||
it("getResolvedValue", async () => {
|
||||
const wrapper = shallow(<SelfServeComponent descriptor={exampleData} />);
|
||||
const wrapper = shallow(
|
||||
<SelfServeComponent descriptor={exampleData} t={undefined} i18n={undefined} tReady={undefined} />
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
const selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||
|
||||
|
@ -179,7 +183,9 @@ describe("SelfServeComponent", () => {
|
|||
|
||||
it("message bar and spinner snapshots", async () => {
|
||||
const newDescriptor = { ...exampleData, onRefresh: onRefreshIsUpdatingMock };
|
||||
let wrapper = shallow(<SelfServeComponent descriptor={newDescriptor} />);
|
||||
let wrapper = shallow(
|
||||
<SelfServeComponent descriptor={newDescriptor} t={undefined} i18n={undefined} tReady={undefined} />
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
let selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||
selfServeComponent.onSaveButtonClick();
|
||||
|
@ -187,7 +193,9 @@ describe("SelfServeComponent", () => {
|
|||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
newDescriptor.onRefresh = onRefreshMock;
|
||||
wrapper = shallow(<SelfServeComponent descriptor={newDescriptor} />);
|
||||
wrapper = shallow(
|
||||
<SelfServeComponent descriptor={newDescriptor} t={undefined} i18n={undefined} tReady={undefined} />
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||
selfServeComponent.onSaveButtonClick();
|
||||
|
|
|
@ -8,15 +8,15 @@ import {
|
|||
Spinner,
|
||||
SpinnerSize,
|
||||
Stack,
|
||||
Text,
|
||||
} from "office-ui-fabric-react";
|
||||
import promiseRetry, { AbortError } from "p-retry";
|
||||
import React from "react";
|
||||
import { Translation } from "react-i18next";
|
||||
import { WithTranslation } from "react-i18next";
|
||||
import * as _ from "underscore";
|
||||
import { sendMessage } from "../Common/MessageHandler";
|
||||
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
||||
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
||||
import "../i18n";
|
||||
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
|
||||
import {
|
||||
AnyDisplay,
|
||||
|
@ -57,7 +57,7 @@ interface PortalNotificationContent {
|
|||
};
|
||||
}
|
||||
|
||||
export interface SelfServeComponentProps {
|
||||
export interface SelfServeComponentProps extends WithTranslation {
|
||||
descriptor: SelfServeDescriptor;
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,9 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||
this.retryIntervalInMs = SelfServeComponent.defaultRetryIntervalInMs;
|
||||
}
|
||||
this.retryOptions = { forever: true, maxTimeout: this.retryIntervalInMs, minTimeout: this.retryIntervalInMs };
|
||||
|
||||
// translation function passed to SelfServeComponent
|
||||
this.translationFunction = this.props.t;
|
||||
}
|
||||
|
||||
private onError = (hasErrors: boolean): void => {
|
||||
|
@ -391,8 +394,8 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||
return this.getTranslation(key, "Common");
|
||||
};
|
||||
|
||||
private getTranslation = (messageKey: string, prefix = `${this.smartUiGeneratorClassName}`): string => {
|
||||
const translationKey = `${prefix}.${messageKey}`;
|
||||
private getTranslation = (messageKey: string, namespace = `${this.smartUiGeneratorClassName}`): string => {
|
||||
const translationKey = `${namespace}:${messageKey}`;
|
||||
const translation = this.translationFunction ? this.translationFunction(translationKey) : messageKey;
|
||||
if (translation === translationKey) {
|
||||
return messageKey;
|
||||
|
@ -441,53 +444,45 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||
|
||||
public render(): JSX.Element {
|
||||
if (this.state.compileErrorMessage) {
|
||||
return <MessageBar messageBarType={MessageBarType.error}>{this.state.compileErrorMessage}</MessageBar>;
|
||||
return (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
<Text>{this.state.compileErrorMessage}</Text>
|
||||
</MessageBar>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Translation>
|
||||
{(translate) => {
|
||||
if (!this.translationFunction) {
|
||||
this.translationFunction = translate;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ overflowX: "auto" }}>
|
||||
<Stack tokens={containerStackTokens}>
|
||||
<Stack.Item>
|
||||
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
|
||||
<Separator styles={separatorStyles} />
|
||||
</Stack.Item>
|
||||
{this.state.isInitializing ? (
|
||||
<Spinner size={SpinnerSize.large} />
|
||||
) : (
|
||||
<>
|
||||
{this.state.notification && (
|
||||
<MessageBar
|
||||
messageBarType={this.state.notification.type}
|
||||
onDismiss={
|
||||
this.state.notification.isCancellable
|
||||
? () => this.setState({ notification: undefined })
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{this.state.notification.message}
|
||||
</MessageBar>
|
||||
)}
|
||||
<SmartUiComponent
|
||||
disabled={this.state.refreshResult?.isUpdateInProgress || this.state.isSaving}
|
||||
descriptor={this.state.root as SmartUiDescriptor}
|
||||
currentValues={this.state.currentValues}
|
||||
onInputChange={this.onInputChange}
|
||||
onError={this.onError}
|
||||
getTranslation={this.getTranslation}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Translation>
|
||||
<div style={{ overflowX: "auto" }}>
|
||||
<Stack tokens={containerStackTokens}>
|
||||
<Stack.Item>
|
||||
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
|
||||
<Separator styles={separatorStyles} />
|
||||
</Stack.Item>
|
||||
{this.state.isInitializing ? (
|
||||
<Spinner size={SpinnerSize.large} />
|
||||
) : (
|
||||
<>
|
||||
{this.state.notification && (
|
||||
<MessageBar
|
||||
messageBarType={this.state.notification.type}
|
||||
onDismiss={
|
||||
this.state.notification.isCancellable ? () => this.setState({ notification: undefined }) : undefined
|
||||
}
|
||||
>
|
||||
<Text>{this.state.notification.message}</Text>
|
||||
</MessageBar>
|
||||
)}
|
||||
<SmartUiComponent
|
||||
disabled={this.state.refreshResult?.isUpdateInProgress || this.state.isSaving}
|
||||
descriptor={this.state.root as SmartUiDescriptor}
|
||||
currentValues={this.state.currentValues}
|
||||
onInputChange={this.onInputChange}
|
||||
onError={this.onError}
|
||||
getTranslation={this.getTranslation}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import { sendMessage } from "../Common/MessageHandler";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
||||
import { appInsights } from "../Shared/appInsights";
|
||||
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { SelfServeTelemetryMessage } from "./SelfServeTypes";
|
||||
|
||||
const action = Action.SelfServe;
|
||||
|
||||
export const trace = (data: SelfServeTelemetryMessage): void => {
|
||||
sendSelfServeTelemetryMessage(ActionModifiers.Mark, data);
|
||||
appInsights.trackEvent({ name: Action[action] }, decorateData(data, ActionModifiers.Mark));
|
||||
};
|
||||
|
||||
export const traceStart = (data: SelfServeTelemetryMessage): number => {
|
||||
const timestamp: number = Date.now();
|
||||
sendSelfServeTelemetryMessage(ActionModifiers.Start, data);
|
||||
appInsights.startTrackEvent(Action[action]);
|
||||
return timestamp;
|
||||
};
|
||||
|
||||
export const traceSuccess = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||
sendSelfServeTelemetryMessage(ActionModifiers.Success, data, timestamp || Date.now());
|
||||
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Success));
|
||||
};
|
||||
|
||||
export const traceFailure = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||
sendSelfServeTelemetryMessage(ActionModifiers.Failed, data, timestamp || Date.now());
|
||||
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Failed));
|
||||
};
|
||||
|
||||
export const traceCancel = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||
sendSelfServeTelemetryMessage(ActionModifiers.Cancel, data, timestamp || Date.now());
|
||||
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Cancel));
|
||||
};
|
||||
|
||||
const sendSelfServeTelemetryMessage = (
|
||||
actionModifier: string,
|
||||
data: SelfServeTelemetryMessage,
|
||||
timeStamp?: number
|
||||
): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dataToSend: any = {
|
||||
type: SelfServeMessageTypes.TelemetryInfo,
|
||||
data: {
|
||||
action: Action[action],
|
||||
actionModifier: actionModifier,
|
||||
data: JSON.stringify(decorateData(data)),
|
||||
},
|
||||
};
|
||||
if (timeStamp) {
|
||||
dataToSend.data.timeStamp = timeStamp;
|
||||
}
|
||||
sendMessage(dataToSend);
|
||||
};
|
||||
|
||||
const decorateData = (data: SelfServeTelemetryMessage, actionModifier?: string) => {
|
||||
return {
|
||||
databaseAccountName: userContext.databaseAccount?.name,
|
||||
defaultExperience: userContext.defaultExperience,
|
||||
authType: userContext.authType,
|
||||
subscriptionId: userContext.subscriptionId,
|
||||
platform: configContext.platform,
|
||||
env: process.env.NODE_ENV,
|
||||
actionModifier,
|
||||
...data,
|
||||
} as { [key: string]: string };
|
||||
};
|
|
@ -98,7 +98,7 @@ export enum NumberUiType {
|
|||
Slider = "Slider",
|
||||
}
|
||||
|
||||
export type ChoiceItem = { label: string; key: string };
|
||||
export type ChoiceItem = { labelTKey: string; key: string };
|
||||
|
||||
export type InputType = number | string | boolean | ChoiceItem | Description;
|
||||
|
||||
|
@ -157,3 +157,9 @@ export interface RefreshResult {
|
|||
export interface RefreshParams {
|
||||
retryIntervalInMs: number;
|
||||
}
|
||||
|
||||
export interface SelfServeTelemetryMessage {
|
||||
selfServeClassName: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data?: any;
|
||||
}
|
||||
|
|
|
@ -131,9 +131,9 @@ describe("SelfServeUtils", () => {
|
|||
type: "object",
|
||||
labelTKey: "Regions",
|
||||
choices: [
|
||||
{ label: "South West US", key: "SWUS" },
|
||||
{ label: "North Central US", key: "NCUS" },
|
||||
{ label: "East US 2", key: "EUS2" },
|
||||
{ labelTKey: "South West US", key: "SWUS" },
|
||||
{ labelTKey: "North Central US", key: "NCUS" },
|
||||
{ labelTKey: "East US 2", key: "EUS2" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -238,9 +238,9 @@ describe("SelfServeUtils", () => {
|
|||
type: "object",
|
||||
labelTKey: "Regions",
|
||||
choices: [
|
||||
{ label: "South West US", key: "SWUS" },
|
||||
{ label: "North Central US", key: "NCUS" },
|
||||
{ label: "East US 2", key: "EUS2" },
|
||||
{ labelTKey: "South West US", key: "SWUS" },
|
||||
{ labelTKey: "North Central US", key: "NCUS" },
|
||||
{ labelTKey: "East US 2", key: "EUS2" },
|
||||
],
|
||||
},
|
||||
children: [] as Node[],
|
||||
|
|
|
@ -195,5 +195,5 @@ export const generateBladeLink = (blade: BladeType): string => {
|
|||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroupName = userContext.resourceGroup;
|
||||
const databaseAccountName = userContext.databaseAccount.name;
|
||||
return `https://portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
|
||||
return `${document.referrer}#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
|
||||
import { trace } from "../SelfServeTelemetryProcessor";
|
||||
import {
|
||||
ChoiceItem,
|
||||
Description,
|
||||
|
@ -147,10 +148,10 @@ const onEnableDedicatedGatewayChange = (
|
|||
};
|
||||
|
||||
const skuDropDownItems: ChoiceItem[] = [
|
||||
{ label: "CosmosD4s", key: CosmosD4s },
|
||||
{ label: "CosmosD8s", key: CosmosD8s },
|
||||
{ label: "CosmosD16s", key: CosmosD16s },
|
||||
{ label: "CosmosD32s", key: CosmosD32s },
|
||||
{ labelTKey: "CosmosD4s", key: CosmosD4s },
|
||||
{ labelTKey: "CosmosD8s", key: CosmosD8s },
|
||||
{ labelTKey: "CosmosD16s", key: CosmosD16s },
|
||||
{ labelTKey: "CosmosD32s", key: CosmosD32s },
|
||||
];
|
||||
|
||||
const getSkus = async (): Promise<ChoiceItem[]> => {
|
||||
|
@ -176,6 +177,8 @@ export default class SqlX extends SelfServeBaseClass {
|
|||
currentValues: Map<string, SmartUiInput>,
|
||||
baselineValues: Map<string, SmartUiInput>
|
||||
): Promise<OnSaveResult> => {
|
||||
trace({ selfServeClassName: "SqlX" });
|
||||
|
||||
const dedicatedGatewayCurrentlyEnabled = currentValues.get("enableDedicatedGateway")?.value as boolean;
|
||||
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -114,6 +114,7 @@ export enum Action {
|
|||
NotebooksGalleryPublicGalleryCount,
|
||||
NotebooksGalleryFavoritesCount,
|
||||
NotebooksGalleryPublishedCount,
|
||||
SelfServe,
|
||||
}
|
||||
|
||||
export const ActionModifiers = {
|
||||
|
|
10
src/i18n.ts
10
src/i18n.ts
|
@ -1,22 +1,14 @@
|
|||
import i18n from "i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import XHR from "i18next-http-backend";
|
||||
import EnglishTranslations from "./Localization/en/translations.json";
|
||||
|
||||
i18n
|
||||
.use(XHR)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources: {
|
||||
en: EnglishTranslations,
|
||||
},
|
||||
fallbackLng: "en",
|
||||
detection: { order: ["navigator", "cookie", "localStorage", "sessionStorage", "querystring", "htmlTag"] },
|
||||
debug: process.env.NODE_ENV === "development",
|
||||
ns: ["translations"],
|
||||
defaultNS: "translations",
|
||||
keySeparator: ".",
|
||||
interpolation: {
|
||||
formatSeparator: ",",
|
||||
|
@ -29,3 +21,5 @@ i18n
|
|||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
|
Loading…
Reference in New Issue