Skip to content

Commit

Permalink
feat: Added Host name validation and support for editing publish prof…
Browse files Browse the repository at this point in the history
…ile (#8131)

* Added Host name validation and support for editing resource configuration

* add logic that  prevents going to next step when host name is invalid

* Removed unnecessary code

* Changes as per PR comments

* Added useCallback for stashWizardState()

* Removed useCallback for stashWizardState() and removed dependency for all the wizards

* Added different callbacks for validation

* Added Choose resources and review resources steps

* Added ReviewResourcesStep

* Changes as per PR comments
  • Loading branch information
VamsiModem committed Jun 22, 2021
1 parent 685ece1 commit 27cc740
Show file tree
Hide file tree
Showing 29 changed files with 645 additions and 142 deletions.
12 changes: 12 additions & 0 deletions extensions/azurePublishNew/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { ResourceGroup } from '@azure/arm-resources/esm/models';
import { DeployLocation, Subscription } from '@botframework-composer/types';
import { ResourceNameAvailability } from '@azure/arm-appservice/esm/models';

export const getSubscriptions = (accessToken: string): Promise<Subscription[]> => {
return new Promise((resolve) => {
Expand All @@ -23,3 +24,14 @@ export const getDeployLocations = (accessToken: string, subscription: string): P
resolve([]);
});
};

export const checkWebAppNameAvailability = async (
token: string,
webAppName: string,
subscriptionId: string
): Promise<ResourceNameAvailability> => {
return {
nameAvailable: true,
message: '',
} as ResourceNameAvailability;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { usePublishApi } from '@bfc/extension-client';

import { Wizard, WizardStep } from '../shared/wizard/Wizard';
import { useResourceConfiguration } from '../../hooks/useResourceConfiguration';
import { userInfoState } from '../../recoilModel/atoms/resourceConfigurationState';
import { userInfoState, enabledResourcesState } from '../../recoilModel/atoms/resourceConfigurationState';
import { useDispatcher } from '../../hooks/useDispatcher';

import { WizardFooter } from './footers/WizardFooter';
import { CreateResourceInstructionsStep } from './steps/CreateResourceInstructionsStep';
Expand All @@ -32,6 +33,8 @@ export const CreateResourcesWizard = React.memo((props: Props) => {
const [isValidResourceConfiguration, setIsValidResourceConfiguration] = useState<boolean>(false);
const { onBack, closeDialog: onCancel } = usePublishApi();
const { stashWizardState } = useResourceConfiguration();
const enabledResources = useRecoilValue(enabledResourcesState);
const { setEnabledResources } = useDispatcher();

React.useEffect(() => {
setSteps([
Expand Down Expand Up @@ -66,7 +69,9 @@ export const CreateResourcesWizard = React.memo((props: Props) => {
),
}
),
onRenderContent: () => <ChooseResourcesStep />,
onRenderContent: () => (
<ChooseResourcesStep enabledResources={enabledResources} onChangeSelection={setEnabledResources} />
),
onCancel,
},
{
Expand All @@ -80,7 +85,7 @@ export const CreateResourcesWizard = React.memo((props: Props) => {
onCancel,
},
]);
}, [isValidResourceConfiguration, userInfo]);
}, [isValidResourceConfiguration, userInfo, enabledResources]);

return (
<Wizard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ import { ImportInstructionsStep } from './steps/ImportInstructionsStep';
import { PublishConfigEditorStep } from './steps/PublishConfigEditorStep';
import { WizardFooter } from './footers/WizardFooter';

const removePlaceholder = (config: any) => {
const removePlaceholder = (config: string) => {
try {
if (config) {
let str = JSON.stringify(config);
str = str.replace(/<[^>]*>/g, '');
const newConfig = JSON.parse(str);
config = config.replace(/<[^>]*>/g, '');
const newConfig = JSON.parse(config);
return newConfig;
} else {
return undefined;
}
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,102 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
export const ChooseResourcesStep = () => {
return <>Add resources step works!</>;
import React, { FC } from 'react';
import { ScrollablePane, ScrollbarVisibility, Stack } from 'office-ui-fabric-react';
import formatMessage from 'format-message';
import { Text } from 'office-ui-fabric-react/lib/Text';
import styled from '@emotion/styled';
import { FluentTheme } from '@uifabric/fluent-theme';
import { usePublishApi } from '@bfc/extension-client';
import { useRecoilValue } from 'recoil';

import { ChooseResourcesList } from '../../shared/ChooseResourcesList';
import { ResourcesItem } from '../../../types';
import { useResourcesApi } from '../../../hooks/useResourcesApi';
import { hostNameState } from '../../../recoilModel/atoms/resourceConfigurationState';
import { useDispatcher } from '../../../hooks/useDispatcher';

const AddResourcesSectionName = styled(Text)`
font-size: ${FluentTheme.fonts.mediumPlus.fontSize};
`;

type Props = {
enabledResources: ResourcesItem[] | undefined;
onChangeSelection?: (requiredItems: ResourcesItem[]) => void;
};

const Root = styled(ScrollablePane)`
height: calc(100vh - 65px);
`;

export const ChooseResourcesStep: FC<Props> = ({ enabledResources, onChangeSelection }) => {
const [items, setItems] = React.useState<ResourcesItem[]>([]);
const { getResourceList, getPreview, getExistingResources } = useResourcesApi();
const { publishConfig } = usePublishApi();
const hostName = useRecoilValue(hostNameState);
const { setRequiredResources } = useDispatcher();
const { currentProjectId, getType } = usePublishApi();

const requiredListItems = items.filter((item) => item.required);
const optionalListItems = items.filter((item) => !item.required);

React.useEffect(() => {
(async () => {
try {
const resources = await getResourceList(currentProjectId(), getType());
const alreadyHave = getExistingResources(publishConfig);

const names = getPreview(hostName);
const result = [];
for (const resource of resources) {
if (alreadyHave.find((item) => item === resource.key)) {
continue;
}
const previewObject = names.find((n) => n.key === resource.key);
result.push({
...resource,
name: previewObject ? previewObject.name : `UNKNOWN NAME FOR ${resource.key}`,
icon: previewObject ? previewObject.icon : undefined,
});
}
setItems(result);
} catch (err) {
// TODO(#8175): Add error handling on the choose resources step
// eslint-disable-next-line no-console
console.log('ERROR', err);
}
})();
}, [getResourceList, getExistingResources, getPreview]);

React.useEffect(() => {
if (items.length > 0) {
setRequiredResources(requiredListItems);
!enabledResources && onChangeSelection(optionalListItems); // select all the optional items by default
}
}, [items]);

return (
<Root data-is-scrollable="true" scrollbarVisibility={ScrollbarVisibility.auto}>
<Stack>
{requiredListItems.length > 0 && (
<>
<AddResourcesSectionName>{formatMessage('Required')}</AddResourcesSectionName>
<ChooseResourcesList items={requiredListItems} />
</>
)}
{optionalListItems.length > 0 && (
<>
<AddResourcesSectionName>{formatMessage('Optional')}</AddResourcesSectionName>
<ChooseResourcesList
items={optionalListItems}
selectedKeys={enabledResources?.map((er) => er.key) ?? []}
onSelectionChanged={(keys) => {
onChangeSelection?.(optionalListItems.filter((item) => keys.includes(item.key)));
}}
/>
</>
)}
</Stack>
</Root>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
IStackItemStyles,
Link,
} from 'office-ui-fabric-react';
import { usePublishApi } from '@bfc/extension-client';

import { useDispatcher } from '../../../hooks/useDispatcher';
import { TenantPicker } from '../../resourceConfiguration/TenantPicker';
Expand Down Expand Up @@ -83,18 +84,28 @@ const urls = {

export const ResourceConfigurationStep = (props: Props) => {
const { setUserInfo } = useDispatcher();

const { publishConfig } = usePublishApi();
const userInfo = useRecoilValue(userInfoState);

const {
configuration: { tenantId, deployLocation, resourceGroupName, subscriptionId, luisRegion, isNewResourceGroup },
handleResourceGroupChange,
handleDeployLocationChange,
handleSubscriptionChange,
handleTenantChange,
handleDeployLocationFetch,
handleLuisRegionChange,
handleHostNameChange,
configuration: {
tenantId,
deployLocation,
resourceGroupName,
subscriptionId,
luisRegion,
isNewResourceGroup,
hostName,
},
handleChangeResourceGroup,
handleChangeDeployLocation,
handleChangeSubscription,
handleValidateHostName,
handleValidateResourceGroupName,
handleChangeTenant,
handleFetchDeployLocation,
handleChangeLuisRegion,
handleChangeHostName,
isValidConfiguration,
deployLocations,
} = useResourceConfiguration();
Expand Down Expand Up @@ -134,14 +145,15 @@ export const ResourceConfigurationStep = (props: Props) => {
</Stack>
<TenantPicker
textFieldProps={{
disabled: !!publishConfig?.tenantId,
styles: autoCompleteTextFieldStyles,
onChange: (e, newValue) => {
if (newValue.length === 0) handleTenantChange('');
if (newValue.length === 0) handleChangeTenant('');
},
}}
value={tenantId}
onClear={() => handleTenantChange('')}
onTenantChange={handleTenantChange}
onChangeTenant={handleChangeTenant}
onClear={() => handleChangeTenant('')}
onUserInfoFetch={setUserInfo}
/>
</Stack>
Expand All @@ -155,14 +167,15 @@ export const ResourceConfigurationStep = (props: Props) => {
<SubscriptionPicker
accessToken={userInfo?.token}
textFieldProps={{
disabled: !!publishConfig?.subscriptionId,
styles: autoCompleteTextFieldStyles,
onChange: (_, newValue) => {
if (newValue.length === 0) handleSubscriptionChange('');
if (newValue.length === 0) handleChangeSubscription('');
},
}}
value={subscriptionId}
onClear={() => handleSubscriptionChange('')}
onSubscriptionChange={handleSubscriptionChange}
onChangeSubscription={handleChangeSubscription}
onClear={() => handleChangeSubscription('')}
/>
</Stack>
<Stack horizontal tokens={configureResourcePropertyStackTokens} verticalAlign="start">
Expand All @@ -181,14 +194,16 @@ export const ResourceConfigurationStep = (props: Props) => {
isNewResourceGroup={isNewResourceGroup}
subscriptionId={subscriptionId}
textFieldProps={{
disabled: !!publishConfig?.resourceGroup?.name,
styles: autoCompleteTextFieldStyles,
onChange: (_, newValue) => {
if (newValue.length === 0) handleResourceGroupChange('', false, false);
if (newValue.length === 0) handleChangeResourceGroup('', false);
},
}}
value={resourceGroupName}
onClear={() => handleResourceGroupChange('', false, false)}
onResourceGroupChange={handleResourceGroupChange}
onChangeResourceGroup={handleChangeResourceGroup}
onClear={() => handleChangeResourceGroup('', false)}
onValidateResourceGroupName={handleValidateResourceGroupName}
/>
</Stack>
<ConfigureResourcesSectionName>{formatMessage('Resource details')}</ConfigureResourcesSectionName>
Expand All @@ -202,8 +217,12 @@ export const ResourceConfigurationStep = (props: Props) => {
</Stack>
<ResourceNameTextField
accessToken={userInfo?.token}
disabled={!!publishConfig?.hostName}
styles={autoCompleteTextFieldStyles}
onHostNameChange={handleHostNameChange}
subscriptionId={subscriptionId}
value={hostName}
onChangeHostName={handleChangeHostName}
onValidateHostName={handleValidateHostName}
/>
</Stack>
<Stack horizontal tokens={configureResourcePropertyStackTokens} verticalAlign="start">
Expand All @@ -215,15 +234,16 @@ export const ResourceConfigurationStep = (props: Props) => {
accessToken={userInfo?.token}
subscriptionId={subscriptionId}
textFieldProps={{
disabled: !!publishConfig?.deployLocation,
styles: autoCompleteTextFieldStyles,
onChange: (_, newValue) => {
if (newValue.length === 0) handleDeployLocationChange('');
if (newValue.length === 0) handleChangeDeployLocation('');
},
}}
value={deployLocation}
onClear={() => handleDeployLocationChange('')}
onDeployLocationChange={handleDeployLocationChange}
onDeployLocationsFetch={handleDeployLocationFetch}
onChangeDeployLocation={handleChangeDeployLocation}
onClear={() => handleChangeDeployLocation('')}
onFetchDeployLocations={handleFetchDeployLocation}
/>
</Stack>
<Stack horizontal tokens={configureResourcePropertyStackTokens} verticalAlign="start">
Expand All @@ -243,14 +263,15 @@ export const ResourceConfigurationStep = (props: Props) => {
.filter((dl) => LuisAuthoringSupportLocation.includes(dl.name))
.map((i) => ({ key: i.name, text: i.displayName }))}
textFieldProps={{
disabled: !!publishConfig?.settings?.luis?.region,
styles: autoCompleteTextFieldStyles,
onChange: (_, newValue) => {
if (newValue.length === 0) handleLuisRegionChange(undefined);
if (newValue.length === 0) handleChangeLuisRegion(undefined);
},
}}
value={luisRegion}
onClear={() => handleLuisRegionChange(undefined)}
onLuisRegionChange={handleLuisRegionChange}
onChangeLuisRegion={handleChangeLuisRegion}
onClear={() => handleChangeLuisRegion(undefined)}
/>
</Stack>
</Stack>
Expand Down
Loading

0 comments on commit 27cc740

Please sign in to comment.