StorageAccountService using authenticated requests

This commit is contained in:
Shiran Pasternak 2022-11-01 17:30:24 -04:00 коммит произвёл Shiran Pasternak
Родитель 55e0746f8e
Коммит 66dd10d76e
20 изменённых файлов: 433 добавлений и 20 удалений

Просмотреть файл

@ -12,6 +12,7 @@ import { DependencyName } from "@batch/ui-common/lib/environment";
import { DefaultFormLayoutProvider, DefaultParameterTypeResolver } from "@batch/ui-react/lib/components/form"; import { DefaultFormLayoutProvider, DefaultParameterTypeResolver } from "@batch/ui-react/lib/components/form";
import { ConsoleLogger } from "@batch/ui-common/lib/logging"; import { ConsoleLogger } from "@batch/ui-common/lib/logging";
import { BrowserDependencyName } from "@batch/ui-react"; import { BrowserDependencyName } from "@batch/ui-react";
import { StorageAccountServiceImpl } from "@batch/ui-service";
import { registerIcons } from "app/config"; import { registerIcons } from "app/config";
import { import {
AuthorizationHttpService, AuthorizationHttpService,
@ -75,6 +76,8 @@ export class AppComponent implements OnInit, OnDestroy {
[DependencyName.Localizer]: () => new StandardLocalizer(), [DependencyName.Localizer]: () => new StandardLocalizer(),
[DependencyName.HttpClient]: [DependencyName.HttpClient]:
() => new BatchExplorerHttpClient(authService), () => new BatchExplorerHttpClient(authService),
[BrowserDependencyName.StorageAccountService]:
() => new StorageAccountServiceImpl(),
[BrowserDependencyName.ParameterTypeResolver]: [BrowserDependencyName.ParameterTypeResolver]:
() => new DefaultParameterTypeResolver(), () => new DefaultParameterTypeResolver(),
[BrowserDependencyName.FormLayoutProvider]: [BrowserDependencyName.FormLayoutProvider]:

Просмотреть файл

@ -1,7 +1,6 @@
export { export {
getEnvironment, getEnvironment,
initEnvironment, initEnvironment,
initMockEnvironment,
destroyEnvironment, destroyEnvironment,
} from "./environment/environment-util"; } from "./environment/environment-util";
export { export {

38
packages/react/package-lock.json сгенерированный
Просмотреть файл

@ -20,6 +20,7 @@
"jest": "^27.1.0", "jest": "^27.1.0",
"jest-axe": "^5.0.1", "jest-axe": "^5.0.1",
"jest-junit": "^12.2.0", "jest-junit": "^12.2.0",
"jest-mock-extended": "^2.0.6",
"ts-jest": "^27.0.5" "ts-jest": "^27.0.5"
}, },
"peerDependencies": { "peerDependencies": {
@ -3102,6 +3103,19 @@
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
} }
}, },
"node_modules/jest-mock-extended": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.6.tgz",
"integrity": "sha512-KoDdjqwIp2phaOWB0hr4O+9HF7hIJx7O+Reefi3iGrNhUpzVkod9UozYTSanvbNvjFYIEH6noA2tIjc8IDpadw==",
"dev": true,
"dependencies": {
"ts-essentials": "^7.0.3"
},
"peerDependencies": {
"jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0",
"typescript": "^3.0.0 || ^4.0.0"
}
},
"node_modules/jest-pnp-resolver": { "node_modules/jest-pnp-resolver": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
@ -4454,6 +4468,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/ts-essentials": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz",
"integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==",
"dev": true,
"peerDependencies": {
"typescript": ">=3.7.0"
}
},
"node_modules/ts-jest": { "node_modules/ts-jest": {
"version": "27.1.4", "version": "27.1.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz",
@ -7222,6 +7245,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"jest-mock-extended": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-2.0.6.tgz",
"integrity": "sha512-KoDdjqwIp2phaOWB0hr4O+9HF7hIJx7O+Reefi3iGrNhUpzVkod9UozYTSanvbNvjFYIEH6noA2tIjc8IDpadw==",
"dev": true,
"requires": {
"ts-essentials": "^7.0.3"
}
},
"jest-pnp-resolver": { "jest-pnp-resolver": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
@ -8257,6 +8289,12 @@
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
}, },
"ts-essentials": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz",
"integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==",
"dev": true
},
"ts-jest": { "ts-jest": {
"version": "27.1.4", "version": "27.1.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz",

Просмотреть файл

@ -68,7 +68,6 @@
"react-dom": ">=16.13.1 <17.0.0", "react-dom": ">=16.13.1 <17.0.0",
"tslib": "~2.3.1" "tslib": "~2.3.1"
}, },
"dependencies": {},
"devDependencies": { "devDependencies": {
"@batch/common-config": "^1.0.0", "@batch/common-config": "^1.0.0",
"@batch/ui-common": "^1.0.0", "@batch/ui-common": "^1.0.0",
@ -84,6 +83,7 @@
"jest": "^27.1.0", "jest": "^27.1.0",
"jest-axe": "^5.0.1", "jest-axe": "^5.0.1",
"jest-junit": "^12.2.0", "jest-junit": "^12.2.0",
"jest-mock-extended": "^2.0.6",
"ts-jest": "^27.0.5" "ts-jest": "^27.0.5"
}, },
"files": [ "files": [

Просмотреть файл

@ -1,4 +1,5 @@
import { render, screen } from "@testing-library/react"; import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import * as React from "react"; import * as React from "react";
import { initMockBrowserEnvironment } from "../../../environment"; import { initMockBrowserEnvironment } from "../../../environment";
import { runAxe } from "../../../test-util/a11y"; import { runAxe } from "../../../test-util/a11y";
@ -23,6 +24,20 @@ describe("Dropdown form control", () => {
const ddEl = screen.getByRole("combobox"); const ddEl = screen.getByRole("combobox");
expect(ddEl).toBeDefined(); expect(ddEl).toBeDefined();
const user = userEvent.setup();
user.click(ddEl);
await waitFor(() =>
expect(ddEl.getAttribute("aria-expanded")).toBe("true")
);
const options = screen.getAllByRole("option");
expect(options.length).toEqual(3);
expect(options.map((option) => option.textContent)).toEqual([
"ace",
"king",
"queen",
]);
expect( expect(
await runAxe(container, { await runAxe(container, {
rules: { rules: {

Просмотреть файл

@ -0,0 +1,118 @@
import { Parameter } from "@batch/ui-common";
import { createForm, Form } from "@batch/ui-common/lib/form";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { UserEvent } from "@testing-library/user-event/dist/types/setup";
import * as React from "react";
import { initMockBrowserEnvironment } from "../../../environment";
import { runAxe } from "../../../test-util/a11y";
import {
StorageAccountDropdown,
} from "../parameter-type";
/* KLUDGE: the parameter has to be called "subscriptionId" until we can specify
* dependencies in parameter types
*/
type FakeFormValues = {
subscriptionId?: string;
storageAccountId?: string;
};
describe("Parameter type tests", () => {
let user: UserEvent;
let form: Form<FakeFormValues>;
let subParam: Parameter<FakeFormValues, "subscriptionId">;
beforeEach(() => {
initMockBrowserEnvironment();
user = userEvent.setup();
form = createForm<FakeFormValues>({ values: {} });
subParam = form.param("subscriptionId", "string");
});
describe("StorageAccountDropdown", () => {
let storageParam: Parameter<FakeFormValues, "storageAccountId">;
beforeEach(() => {
storageParam = form.param("storageAccountId", "string");
});
test("simple dropdown", async () => {
render(<StorageAccountDropdown param={storageParam} />);
const element = screen.getByRole("combobox");
await user.click(element);
await waitFor(() => expectElementEnabled(element));
expect(screen.queryAllByRole("option")).toEqual([]);
});
test("dropdown with subscription", async () => {
render(
<>
<SubscriptionDropdown param={subParam} />
<StorageAccountDropdown param={storageParam} />
</>
);
const subDropdown = screen.getByRole("combobox", {
name: /subscriptionId/,
});
const storageDropdown = screen.getByRole("combobox", {
name: /storageAccountId/,
});
await user.click(subDropdown);
await waitFor(() => {
expectElementEnabled(subDropdown);
expectElementEnabled(storageDropdown);
});
selectOption(0);
await user.click(storageDropdown);
let storageAccounts = await screen.findAllByRole("option");
// Data served by FakeStorageAccountService
expect(storageAccounts.length).toEqual(3);
expect(storageAccounts[0].textContent).toEqual("Storage A");
await user.click(subDropdown); // Reopen sub dropdown
selectOption(2);
await user.click(storageDropdown);
storageAccounts = await screen.findAllByRole("option");
expect(storageAccounts.length).toEqual(4);
expect(storageAccounts[0].textContent).toEqual("Storage F");
});
test("bad subscription shows error", async () => {
render(
<>
<SubscriptionDropdown param={subParam} />
<StorageAccountDropdown param={storageParam} />
</>
);
const subDropdown = screen.getByRole("combobox", {
name: /subscriptionId/,
});
const storageDropdown = screen.getByRole("combobox", {
name: /storageAccountId/,
});
await user.click(subDropdown);
await waitFor(() => {
expectElementEnabled(subDropdown);
expectElementEnabled(storageDropdown);
});
selectOption(4); // Bad subscription
expect(screen.getByText("Bad Subscription")).toBeDefined();
await user.click(storageDropdown);
expect(await screen.queryAllByRole("option")).toEqual([]);
expect(
screen.getByText("Error: No storage accounts in subscription.")
).toBeDefined();
});
});
});
const expectElementEnabled = (element: HTMLElement) =>
expect(element.className).not.toContain("is-disabled");
const selectOption = (index: number) =>
fireEvent.click(screen.getAllByRole("option")[index]);

Просмотреть файл

@ -1,10 +1,16 @@
import { ParameterType as CommonParameterType } from "@batch/ui-common"; import {
Parameter,
ParameterType as CommonParameterType,
} from "@batch/ui-common";
import { inject } from "@batch/ui-common/lib/environment";
import { FormValues, ValidationStatus } from "@batch/ui-common/lib/form";
import { StorageAccount, StorageAccountService } from "@batch/ui-service";
import * as React from "react"; import * as React from "react";
import { Parameter } from "@batch/ui-common"; import { useEffect, useState } from "react";
import { TextField } from "./text-field"; import { BrowserDependencyName } from "../..";
import { Dropdown } from "./dropdown";
import { useAsyncEffect, useUniqueId } from "../../hooks"; import { useAsyncEffect, useUniqueId } from "../../hooks";
import { FormValues } from "@batch/ui-common/lib/form"; import { Dropdown } from "./dropdown";
import { TextField } from "./text-field";
enum ExtendedParameterType { enum ExtendedParameterType {
BatchAccountName = "BatchAccountName", BatchAccountName = "BatchAccountName",
@ -99,7 +105,7 @@ export class DefaultParameterTypeResolver implements ParameterTypeResolver {
); );
case ParameterType.StorageAccountId: case ParameterType.StorageAccountId:
return ( return (
<StringParamTextField <StorageAccountDropdown
id={id} id={id}
key={param.name} key={param.name}
param={param} param={param}
@ -107,7 +113,7 @@ export class DefaultParameterTypeResolver implements ParameterTypeResolver {
); );
case ParameterType.SubscriptionId: case ParameterType.SubscriptionId:
return ( return (
<SubscriptionIdParamDropdown <SubscriptionDropdown
id={id} id={id}
key={param.name} key={param.name}
param={param} param={param}
@ -157,6 +163,68 @@ export function StringParamTextField<
); );
} }
export function StorageAccountDropdown<
V extends FormValues,
K extends Extract<keyof V, string>
>(props: ParamControlProps<V, K>): JSX.Element {
const { param } = props;
const value = param.value == null ? undefined : String(param.value);
const [loading, setLoading] = useState<boolean>(true);
const [storageAccounts, setStorageAccounts] = useState<StorageAccount[]>(
[]
);
const id = useUniqueId("form-control", props.id);
const service: StorageAccountService = inject(
BrowserDependencyName.StorageAccountService
);
const form = param.parentForm;
const [subscriptionId, setSubscriptionId] = useState<string>(
form.values.subscriptionId as string
);
const [validationStatus, setValidationStatus] =
useState<ValidationStatus | null>();
useAsyncEffect(async () => {
let accounts: StorageAccount[] = [];
try {
if (subscriptionId) {
accounts = await service.list(subscriptionId);
}
setValidationStatus(null);
} catch (error) {
setValidationStatus(new ValidationStatus("error", error + ""));
} finally {
setStorageAccounts(accounts);
setLoading(false);
}
}, [subscriptionId]);
useEffect(() => {
const handler = form.onChange((values: FormValues) =>
setSubscriptionId(values.subscriptionId as string)
);
return () => form.removeOnChange(handler);
});
const options = storageAccounts.map((sub) => {
return { value: sub.id, label: sub.name };
});
return (
<Dropdown
id={id}
label={param.label}
disabled={loading || param.disabled}
options={options}
placeholder={param.placeholder}
value={value}
validationStatus={validationStatus ?? param.validationStatus}
onChange={(value: string) => (param.value = value as V[K])}
/>
);
}
export function SubscriptionIdParamDropdown< export function SubscriptionIdParamDropdown<
V extends FormValues, V extends FormValues,
K extends Extract<keyof V, string> K extends Extract<keyof V, string>
@ -183,7 +251,7 @@ export function SubscriptionIdParamDropdown<
resolve(); resolve();
}, 1000); }, 1000);
}); });
}); }, []);
const options = subscriptions.map((sub) => { const options = subscriptions.map((sub) => {
return { value: sub.id, label: sub.displayName }; return { value: sub.id, label: sub.displayName };
@ -197,9 +265,7 @@ export function SubscriptionIdParamDropdown<
options={options} options={options}
placeholder={param.placeholder} placeholder={param.placeholder}
value={value} value={value}
onChange={(newValue: string) => { onChange={(newValue: string) => (param.value = newValue as V[K])}
param.value = newValue as V[K];
}}
/> />
); );
} }

Просмотреть файл

@ -8,6 +8,7 @@ import {
getEnvironment, getEnvironment,
} from "@batch/ui-common/lib/environment"; } from "@batch/ui-common/lib/environment";
import { FormValues } from "@batch/ui-common/lib/form"; import { FormValues } from "@batch/ui-common/lib/form";
import { StorageAccountService } from "@batch/ui-service";
import { import {
FormLayout, FormLayout,
FormLayoutProvider, FormLayoutProvider,
@ -23,6 +24,7 @@ import { MockBrowserEnvironment } from "./mock-browser-environment";
export enum BrowserDependencyName { export enum BrowserDependencyName {
ParameterTypeResolver = "parameterTypeResolver", ParameterTypeResolver = "parameterTypeResolver",
FormLayoutProvider = "formLayoutProvider", FormLayoutProvider = "formLayoutProvider",
StorageAccountService = "storageAccount",
} }
export interface BrowserEnvironment export interface BrowserEnvironment
@ -42,6 +44,7 @@ export interface BrowserEnvironmentConfig extends EnvironmentConfig {
export interface BrowserDependencyFactories extends DependencyFactories { export interface BrowserDependencyFactories extends DependencyFactories {
[BrowserDependencyName.ParameterTypeResolver]: () => ParameterTypeResolver; [BrowserDependencyName.ParameterTypeResolver]: () => ParameterTypeResolver;
[BrowserDependencyName.FormLayoutProvider]: () => FormLayoutProvider; [BrowserDependencyName.FormLayoutProvider]: () => FormLayoutProvider;
[BrowserDependencyName.StorageAccountService]: () => StorageAccountService;
} }
/** /**

Просмотреть файл

@ -5,6 +5,7 @@ import {
mockDependencyFactories, mockDependencyFactories,
mockEnvironmentConfig, mockEnvironmentConfig,
} from "@batch/ui-common/lib/environment"; } from "@batch/ui-common/lib/environment";
import { FakeStorageAccountService } from "@batch/ui-service";
import { initializeIcons } from "@fluentui/react/lib/Icons"; import { initializeIcons } from "@fluentui/react/lib/Icons";
import { BrowserEnvironmentConfig } from "."; import { BrowserEnvironmentConfig } from ".";
import { import {
@ -17,12 +18,9 @@ import { MockBrowserEnvironment } from "./mock-browser-environment";
let _fluentIconsInitialized = false; let _fluentIconsInitialized = false;
export const mockBrowserDepFactories: Partial<BrowserDependencyFactories> = { export const mockBrowserDepFactories: Partial<BrowserDependencyFactories> = {
parameterTypeResolver: () => { parameterTypeResolver: () => new DefaultParameterTypeResolver(),
return new DefaultParameterTypeResolver(); formLayoutProvider: () => new DefaultFormLayoutProvider(),
}, storageAccount: () => new FakeStorageAccountService(),
formLayoutProvider: () => {
return new DefaultFormLayoutProvider();
},
}; };
/** /**

Просмотреть файл

@ -0,0 +1,4 @@
export interface ArmResourceListResponse<T> {
value: T[];
nextLink?: string;
}

Просмотреть файл

@ -0,0 +1,15 @@
export const Endpoints = {
arm: `https://management.azure.com`,
};
export const ApiVersion = {
arm: `2022-05-01`,
batch: {
arm: `2022-06-01`,
data: `2022-06-01`,
},
storage: {
arm: `2022-05-01`,
data: `2022-05-01`,
},
};

Просмотреть файл

@ -1,3 +1,5 @@
export * from "./http-service"; export * from "./http-service";
export * from "./view"; export * from "./view";
export * from "./certificate"; export * from "./certificate";
export * from "./storage";
export * from "./subscription";

Просмотреть файл

@ -0,0 +1,46 @@
import {
initMockEnvironment,
getMockEnvironment,
} from "@batch/ui-common/lib/environment";
import { MockHttpClient, MockHttpResponse } from "@batch/ui-common/lib/http";
import { StorageAccountServiceImpl } from "..";
import { ApiVersion, Endpoints } from "../../constants";
describe("StorageAccountService", () => {
let httpClient: MockHttpClient;
beforeEach(() => {
initMockEnvironment();
httpClient = getMockEnvironment().getHttpClient();
});
afterEach(() => {
const assertions = httpClient.remainingAssertions();
if (assertions.length > 0) {
throw new Error(
`HTTP client has untested assertions (${assertions.join(", ")})`
);
}
});
test("list()", async () => {
const service = new StorageAccountServiceImpl();
httpClient.addExpected(
new MockHttpResponse(
`${Endpoints.arm}/subscriptions//fake/sub1/providers/Microsoft.Storage/storageAccounts?api-version=${ApiVersion.storage.arm}`,
200,
JSON.stringify({
value: [
{ id: "1", name: "One" },
{ id: "2", name: "Two" },
{ id: "3", name: "Three" },
],
})
)
);
const accounts = await service.list("/fake/sub1");
expect(accounts.length).toEqual(3);
});
});

Просмотреть файл

@ -0,0 +1,47 @@
import { StorageAccount } from "./storage-account-models";
import { StorageAccountService } from "./storage-account-service";
const subscriptionAccounts: { [key: string]: StorageAccount[] } = {
"/fake/sub1": [
{ id: "/fake/storageA", name: "Storage A" },
{ id: "/fake/storageB", name: "Storage B" },
{ id: "/fake/storageC", name: "Storage C" },
],
"/fake/sub2": [
{ id: "/fake/storageD", name: "Storage D" },
{ id: "/fake/storageE", name: "Storage E" },
],
"/fake/sub3": [
{ id: "/fake/storageF", name: "Storage F" },
{ id: "/fake/storageG", name: "Storage G" },
{ id: "/fake/storageH", name: "Storage H" },
{ id: "/fake/storageI", name: "Storage I" },
],
};
export class FakeStorageAccountService implements StorageAccountService {
public async list(subscriptionId: string): Promise<StorageAccount[]> {
if (subscriptionId in subscriptionAccounts) {
return subscriptionAccounts[subscriptionId];
} else if (subscriptionId === "/fake/badsub") {
// Simulates a network error.
throw new Error("No storage accounts in subscription.");
} else {
return [];
}
}
public async get(): Promise<StorageAccount | null> {
return null;
}
public async create(): Promise<void> {
return;
}
public async remove(): Promise<void> {
return;
}
public async update(): Promise<void> {
return;
}
}

Просмотреть файл

@ -0,0 +1,3 @@
export * from "./storage-account-service";
export * from "./storage-account-models";
export * from "./fake-storage-account-service";

Просмотреть файл

@ -0,0 +1,4 @@
export interface StorageAccount {
id: string;
name: string;
}

Просмотреть файл

@ -0,0 +1,41 @@
import { ApiVersion, Endpoints } from "../constants";
import { AbstractHttpService } from "../http-service";
import { ArmResourceListResponse } from "../arm";
import { StorageAccount } from "./storage-account-models";
export interface StorageAccountService {
list(subscriptionId: string): Promise<StorageAccount[]>;
get(id: string): Promise<StorageAccount | null>;
create(account: StorageAccount): Promise<void>;
remove(account: StorageAccount): Promise<void>;
update(account: StorageAccount): Promise<void>;
}
export class StorageAccountServiceImpl
extends AbstractHttpService
implements StorageAccountService
{
public async list(subscriptionId: string): Promise<StorageAccount[]> {
const response = await this.httpClient.get(
`${Endpoints.arm}/subscriptions/${subscriptionId}/providers/Microsoft.Storage/storageAccounts?api-version=${ApiVersion.storage.arm}`,
{}
);
const json =
(await response.json()) as ArmResourceListResponse<StorageAccount>;
return json.value;
}
public async get(): Promise<StorageAccount | null> {
return null;
}
public async create(): Promise<void> {
return;
}
public async remove(): Promise<void> {
return;
}
public async update(): Promise<void> {
return;
}
}

Просмотреть файл

@ -0,0 +1 @@
export * from "./subscription-models";

Просмотреть файл

@ -0,0 +1,7 @@
export interface Subscription {
id: string;
subscriptionId: string;
tenantId: string;
displayName: string;
state: string;
}

Просмотреть файл

@ -14,6 +14,7 @@ import {
DefaultParameterTypeResolver, DefaultParameterTypeResolver,
} from "@batch/ui-react/lib/components/form"; } from "@batch/ui-react/lib/components/form";
import { StandardLocalizer } from "@batch/ui-common/lib/localization"; import { StandardLocalizer } from "@batch/ui-common/lib/localization";
import { FakeStorageAccountService } from "@batch/ui-service";
// Defined by webpack // Defined by webpack
declare const ENV: { declare const ENV: {
@ -30,6 +31,8 @@ export function init(rootEl: HTMLElement): void {
[DependencyName.Logger]: () => new ConsoleLogger(), [DependencyName.Logger]: () => new ConsoleLogger(),
[DependencyName.Localizer]: () => new StandardLocalizer(), [DependencyName.Localizer]: () => new StandardLocalizer(),
[DependencyName.HttpClient]: () => new MockHttpClient(), [DependencyName.HttpClient]: () => new MockHttpClient(),
[BrowserDependencyName.StorageAccountService]: () =>
new FakeStorageAccountService(),
[BrowserDependencyName.ParameterTypeResolver]: () => [BrowserDependencyName.ParameterTypeResolver]: () =>
new DefaultParameterTypeResolver(), new DefaultParameterTypeResolver(),
[BrowserDependencyName.FormLayoutProvider]: () => [BrowserDependencyName.FormLayoutProvider]: () =>