Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: save extension state #6309

Merged
merged 26 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
27fddbb
save name and target state
VanyLaw Mar 5, 2021
2d6bfab
Merge remote-tracking branch 'origin/main' into wenyluo/fix
VanyLaw Mar 8, 2021
ee9db73
save extension status
VanyLaw Mar 8, 2021
2442940
Merge branch 'main' into wenyluo/fix
luhan2017 Mar 8, 2021
6a0e2f1
remove annotation
VanyLaw Mar 8, 2021
6ba1bd8
Merge branch 'wenyluo/fix' of https://github.com/microsoft/BotFramewo…
VanyLaw Mar 8, 2021
2694e26
Merge remote-tracking branch 'origin/main' into wenyluo/fix
VanyLaw Mar 9, 2021
62efde6
fix comments
VanyLaw Mar 9, 2021
11e3bbb
fix conflict
VanyLaw Mar 9, 2021
05bdda8
Merge branch 'main' into wenyluo/fix
benbrown Mar 9, 2021
bc7f820
Merge branch 'main' into wenyluo/fix
luhan2017 Mar 10, 2021
c930513
Merge branch 'main' into wenyluo/fix
benbrown Mar 10, 2021
cbecc39
Merge branch 'main' into wenyluo/fix
tonyanziano Mar 10, 2021
ccb0773
Merge branch 'main' into wenyluo/fix
benbrown Mar 10, 2021
f7e98aa
Merge branch 'main' into wenyluo/fix
srinaath Mar 10, 2021
025cb97
use localStorage instead of setExtensionState to parent component to …
VanyLaw Mar 11, 2021
7e1ae1a
Merge branch 'main' into wenyluo/fix
boydc2014 Mar 12, 2021
b55d5b4
remove setExtensionState, add useLocalStorage
VanyLaw Mar 12, 2021
800899b
Merge branch 'wenyluo/fix' of https://github.com/microsoft/BotFramewo…
VanyLaw Mar 12, 2021
0535572
polish
VanyLaw Mar 12, 2021
8e3b290
Merge remote-tracking branch 'origin/main' into wenyluo/fix
VanyLaw Mar 15, 2021
8b57d4c
update uselocalStorage, use hooks in extension to save state;
VanyLaw Mar 15, 2021
642b86c
Merge remote-tracking branch 'origin/main' into wenyluo/fix
VanyLaw Mar 17, 2021
2c2a087
fix comments
VanyLaw Mar 17, 2021
6546249
polish
VanyLaw Mar 17, 2021
c8e6272
Merge branch 'main' into wenyluo/fix
VanyLaw Mar 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { jsx, css } from '@emotion/core';
import formatMessage from 'format-message';
import { SharedColors } from '@uifabric/fluent-theme';
import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { useState, useMemo, useCallback, Fragment, useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { useState, useMemo, useCallback, Fragment } from 'react';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { Separator } from 'office-ui-fabric-react/lib/Separator';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
Expand All @@ -17,21 +16,18 @@ import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip';
import { Icon } from 'office-ui-fabric-react/lib/Icon';

import { separator } from '../../publish/styles';
import { armScopes, graphScopes } from '../../../constants';
import { PublishType } from '../../../recoilModel/types';
import { PluginAPI } from '../../../plugins/api';
import { dispatcherState } from '../../../recoilModel';
import { AuthClient } from '../../../utils/authClient';
import { getTokenFromCache, isGetTokenFromUser } from '../../../utils/auth';

type ProfileFormDialogProps = {
onDismiss: () => void;
targets: PublishTarget[];
types: PublishType[];
onNext: () => void;
updateSettings: (name: string, type: string, configuration: string) => Promise<void>;
projectId: string;
setType: (value) => void;
name: string;
targetType: string;
setName: (value: string) => void;
setTargetType: (value: string) => void;
current?: { index: number; item: PublishTarget } | null;
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
};
const labelContainer = css`
Expand Down Expand Up @@ -72,11 +68,8 @@ const onRenderLabel = (props) => {
};

export const ProfileFormDialog: React.FC<ProfileFormDialogProps> = (props) => {
const { onDismiss, targets, types, onNext, updateSettings, projectId, setType, current } = props;
const [name, setName] = useState(current?.item.name || '');
const { name, setName, targetType, setTargetType, onDismiss, targets, types, onNext, setType, current } = props;
const [errorMessage, setErrorMsg] = useState('');
const [targetType, setTargetType] = useState<string>(current?.item.type || '');
const { provisionToTarget } = useRecoilValue(dispatcherState);

const updateName = (e, newName) => {
setName(newName);
Expand Down Expand Up @@ -116,36 +109,6 @@ export const ProfileFormDialog: React.FC<ProfileFormDialogProps> = (props) => {
return !targetType || !name || !!errorMessage;
}, [errorMessage, name, targetType]);

// pass functions to extensions
useEffect(() => {
PluginAPI.publish.getType = () => {
return targetType;
};
PluginAPI.publish.getSchema = () => {
return types.find((t) => t.name === targetType)?.schema;
};
PluginAPI.publish.savePublishConfig = (config) => {
updateSettings(name, targetType, JSON.stringify(config) || '{}');
};
}, [targetType, name, types, updateSettings]);

useEffect(() => {
PluginAPI.publish.startProvision = async (config) => {
const fullConfig = { ...config, name: name, type: targetType };
let arm, graph;
if (!isGetTokenFromUser()) {
// login or get token implicit
arm = await AuthClient.getAccessToken(armScopes);
graph = await AuthClient.getAccessToken(graphScopes);
} else {
// get token from cache
arm = getTokenFromCache('accessToken');
graph = getTokenFromCache('graphToken');
}
provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item);
};
}, [name, targetType]);

return (
<Fragment>
<Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,30 @@ type PublishProfileDialogProps = {
setPublishTargets: (targets: PublishTarget[], projectId: string) => Promise<void>;
};

type ExtensionState = AzureExtensionState; // can expand to pva like | PVAExtensionState

type AzureExtensionState = {
subscriptionId: string;
resourceGroup: string;
hostName: string;
location: string;
luisLocation: string;
enableResources: any;
requireResources: any;
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
choice: any;
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
};

const Page = {
ProfileForm: Symbol('form'),
ConfigProvision: Symbol('config'),
};

export const PublishProfileDialog: React.FC<PublishProfileDialogProps> = (props) => {
const { current, types, projectId, closeDialog, targets, setPublishTargets } = props;
const [name, setName] = useState(current?.item.name || '');
const [targetType, setTargetType] = useState<string>(current?.item.type || '');
const [extensionState, setExtensionState] = useState<ExtensionState | undefined>();

const [page, setPage] = useState(Page.ProfileForm);
const [publishSurfaceStyles, setStyles] = useState(defaultPublishSurface);
const { provisionToTarget } = useRecoilValue(dispatcherState);
Expand Down Expand Up @@ -93,6 +110,9 @@ export const PublishProfileDialog: React.FC<PublishProfileDialogProps> = (props)
PluginAPI.publish.setTitle = (value) => {
setTitle(value);
};
PluginAPI.publish.setExtensionState = (value) => {
setExtensionState(value);
};
}, []);

// setup plugin APIs so that the provisioning plugin can initiate the process from inside the iframe
Expand All @@ -101,12 +121,15 @@ export const PublishProfileDialog: React.FC<PublishProfileDialogProps> = (props)
PluginAPI.publish.currentProjectId = () => {
return projectId;
};
PluginAPI.publish.getExtensionState = () => {
return extensionState;
};
if (current?.item.type) {
setPage(Page.ConfigProvision);
} else {
setPage(Page.ProfileForm);
}
}, [current, projectId]);
}, [current, projectId, extensionState]);

const savePublishTarget = useCallback(
async (name: string, type: string, configuration: string) => {
Expand All @@ -125,32 +148,34 @@ export const PublishProfileDialog: React.FC<PublishProfileDialogProps> = (props)
);

useEffect(() => {
if (current?.item?.type) {
PluginAPI.publish.getType = () => {
return current?.item?.type;
};
PluginAPI.publish.getSchema = () => {
return types.find((t) => t.name === current?.item?.type)?.schema;
};
PluginAPI.publish.savePublishConfig = (config) => {
savePublishTarget(current?.item.name, current?.item?.type, JSON.stringify(config) || '{}');
};
PluginAPI.publish.startProvision = async (config) => {
const fullConfig = { ...config, name: current.item.name, type: current.item.type };
let arm, graph;
if (!isGetTokenFromUser()) {
// login or get token implicit
arm = await AuthClient.getAccessToken(armScopes);
graph = await AuthClient.getAccessToken(graphScopes);
} else {
// get token from cache
arm = getTokenFromCache('accessToken');
graph = getTokenFromCache('graphToken');
}
provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item);
};
}
}, [current, types, savePublishTarget]);
PluginAPI.publish.startProvision = async (config) => {
const fullConfig = { ...config, name: name, type: targetType };
let arm, graph;
if (!isGetTokenFromUser()) {
// login or get token implicit
arm = await AuthClient.getAccessToken(armScopes);
graph = await AuthClient.getAccessToken(graphScopes);
} else {
// get token from cache
arm = getTokenFromCache('accessToken');
graph = getTokenFromCache('graphToken');
}
provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item);
};
}, [name, targetType, projectId]);

// pass functions to extensions
useEffect(() => {
PluginAPI.publish.getType = () => {
return targetType;
};
PluginAPI.publish.getSchema = () => {
return types.find((t) => t.name === targetType)?.schema;
};
PluginAPI.publish.savePublishConfig = (config) => {
savePublishTarget(name, targetType, JSON.stringify(config) || '{}');
};
}, [targetType, name, types, savePublishTarget]);

return (
<Fragment>
Expand All @@ -176,11 +201,13 @@ export const PublishProfileDialog: React.FC<PublishProfileDialogProps> = (props)
</div>
<ProfileFormDialog
current={current}
projectId={projectId}
name={name}
setName={setName}
setTargetType={setTargetType}
setType={setSelectType}
targets={targets}
targetType={targetType}
types={types}
updateSettings={savePublishTarget}
onDismiss={closeDialog}
onNext={() => {
setPage(Page.ConfigProvision);
Expand Down
4 changes: 4 additions & 0 deletions Composer/packages/client/src/plugins/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ interface PublishAPI {
savePublishConfig?: (config: PublishConfig) => void;
getTokenFromCache?: () => { accessToken: string; graphToken: string };
isGetTokenFromUser?: () => boolean;
getExtensionState?: () => any;
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
setExtensionState?: (value: any) => void;
}

class API implements IAPI {
Expand All @@ -59,6 +61,8 @@ class API implements IAPI {
savePublishConfig: undefined,
getTokenFromCache: undefined,
isGetTokenFromUser: undefined,
getExtensionState: undefined,
setExtensionState: undefined,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ export function usePublishApi() {
function isGetTokenFromUser(): boolean {
return window[ComposerGlobalName].isGetTokenFromUser();
}

function getExtensionState(): any {
return window[ComposerGlobalName].getExtensionState();
}
function setExtensionState(value: any) {
return window[ComposerGlobalName].setExtensionState(value);
}
return {
publishConfig: getPublishConfig(),
startProvision,
Expand All @@ -62,5 +67,7 @@ export function usePublishApi() {
savePublishConfig,
getTokenFromCache,
isGetTokenFromUser,
getExtensionState,
setExtensionState,
};
}
43 changes: 32 additions & 11 deletions extensions/azurePublish/src/components/azureProvisionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { AzureResourceTypes, ResourcesItem, authConfig } from '../types';
import {
getResourceList,
getSubscriptions,
getResourceGroups,
getDeployLocations,
getPreview,
getLuisAuthoringRegions,
Expand Down Expand Up @@ -306,10 +305,13 @@ export const AzureProvisionDialog: React.FC = () => {
getType,
getTokenFromCache,
isGetTokenFromUser,
setExtensionState,
getExtensionState,
} = usePublishApi();
// set type of publish - azurePublish or azureFunctionsPublish
const publishType = getType();
const currentConfig = removePlaceholder(publishConfig);
const extensionState = getExtensionState();
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved

const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);
const [deployLocations, setDeployLocations] = useState<DeployLocation[]>([]);
Expand All @@ -318,17 +320,19 @@ export const AzureProvisionDialog: React.FC = () => {
const [token, setToken] = useState<string>();
const [currentUser, setCurrentUser] = useState<any>();

const [choice, setChoice] = useState(choiceOptions[0]);
const [currentSubscription, setSubscription] = useState<string>('');
const [currentResourceGroup, setResourceGroup] = useState<string>('');
const [currentHostName, setHostName] = useState('');
const [choice, setChoice] = useState(extensionState?.choice || choiceOptions[0]);
const [currentSubscription, setSubscription] = useState<string>(extensionState?.subscriptionId || '');
const [currentResourceGroup, setResourceGroup] = useState<string>(extensionState?.resourceGroup || '');
const [currentHostName, setHostName] = useState(extensionState?.hostName || '');
const [errorHostName, setErrorHostName] = useState('');
const [errorResourceGroupName, setErrorResourceGroupName] = useState('');
const [currentLocation, setLocation] = useState<string>(currentConfig?.region);
const [currentLuisLocation, setCurrentLuisLocation] = useState<string>(currentConfig?.settings?.luis?.region);
const [currentLocation, setLocation] = useState<string>(extensionState?.location || currentConfig?.region);
const [currentLuisLocation, setCurrentLuisLocation] = useState<string>(
extensionState?.luisLocation || currentConfig?.settings?.luis?.region
);
const [extensionResourceOptions, setExtensionResourceOptions] = useState<ResourcesItem[]>([]);
const [enabledResources, setEnabledResources] = useState<ResourcesItem[]>([]); // create from optional list
const [requireResources, setRequireResources] = useState<ResourcesItem[]>([]);
const [enabledResources, setEnabledResources] = useState<ResourcesItem[]>(extensionState?.enableResources || []); // create from optional list
const [requireResources, setRequireResources] = useState<ResourcesItem[]>(extensionState?.requireResources || []);

const [isEditorError, setEditorError] = useState(false);
const [importConfig, setImportConfig] = useState<any>();
Expand Down Expand Up @@ -624,7 +628,7 @@ export const AzureProvisionDialog: React.FC = () => {

const PageFormConfig = (
<Fragment>
<ChoiceGroup defaultSelectedKey="create" options={choiceOptions} style={{}} onChange={updateChoice} />
<ChoiceGroup defaultSelectedKey={choice.key} options={choiceOptions} style={{}} onChange={updateChoice} />
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
{subscriptionOption?.length > 0 && choice.key === 'create' && (
<form style={{ width: '50%', marginTop: '16px' }}>
<Dropdown
Expand Down Expand Up @@ -814,7 +818,24 @@ export const AzureProvisionDialog: React.FC = () => {
/>
) : null}
<div>
<DefaultButton style={{ margin: '0 4px' }} text={'Back'} onClick={onBack} />
<DefaultButton
style={{ margin: '0 4px' }}
text={'Back'}
VanyLaw marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => {
// setExtensionState
setExtensionState({
subscriptionId: currentSubscription,
resourceGroup: currentResourceGroup,
hostName: currentHostName,
location: currentLocation,
luisLocation: currentLuisLocation,
enableResources: enabledResources,
requireResources: requireResources,
choice: choice,
});
onBack();
}}
/>
{choice.key === 'create' ? (
<PrimaryButton
disabled={isDisAble}
Expand Down