Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion Composer/packages/client/src/plugins/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface AuthAPI {
getAccessToken: (options: AuthParameters) => Promise<string>; // returns an access token
getARMTokenForTenant: (tenantId: string) => Promise<string>; // returns an ARM access token for specified tenant
getTenants: () => Promise<AzureTenant[]>; // signs a user in and returns available Azure tenants for the user
logOut: () => Promise<void>;
logOut: () => Promise<boolean>;
}

interface PublishAPI {
Expand Down
14 changes: 14 additions & 0 deletions Composer/packages/client/src/shell/useShell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
BotInProject,
FeatureFlagKey,
SDKKinds,
Notification,
} from '@botframework-composer/types';
import { useRecoilValue } from 'recoil';
import formatMessage from 'format-message';
Expand Down Expand Up @@ -48,6 +49,7 @@ import { navigateTo } from '../utils/navigation';
import TelemetryClient from '../telemetry/TelemetryClient';
import { lgFilesSelectorFamily } from '../recoilModel/selectors/lg';
import { getMemoryVariables } from '../recoilModel/dispatchers/utils/project';
import { createNotification } from '../recoilModel/dispatchers/notification';

import { useLgApi } from './lgApi';
import { useLuApi } from './luApi';
Expand Down Expand Up @@ -127,6 +129,10 @@ export function useShell(source: EventSource, projectId: string): Shell {
reloadProject,
setApplicationLevelError,
updateRecognizer,
addNotification,
deleteNotification,
hideNotification,
markNotificationAsRead,
} = useRecoilValue(dispatcherState);

const lgApi = useLgApi(projectId);
Expand Down Expand Up @@ -279,6 +285,14 @@ export function useShell(source: EventSource, projectId: string): Shell {
confirm: OpenConfirmModal,
telemetryClient: TelemetryClient,
getMemoryVariables,
addNotification: (notificationWithoutId: Notification): string => {
const notification = createNotification(notificationWithoutId);
addNotification(notification);
return notification.id;
},
deleteNotification,
markNotificationAsRead,
hideNotification,
};

const currentDialog = useMemo(() => {
Expand Down
14 changes: 7 additions & 7 deletions Composer/packages/client/src/utils/authClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { promises } from 'fs';

import { AuthParameters, AzureTenant } from '@botframework-composer/types';

import { authConfig } from '../constants';
Expand Down Expand Up @@ -96,18 +98,16 @@ async function logOut() {
cleanTokenFromCache('accessToken');
cleanTokenFromCache('graphToken');
if (isElectron()) {
try {
const url = '/api/auth/logOut';
await fetch(url, { method: 'GET' });
} catch (e) {
// error handling
console.error('Can not log out');
}
const url = '/api/auth/logOut';
const result = await fetch(url, { method: 'GET' });
return result.ok;
} else if (authConfig.clientId) {
// clean token cache in storage
cleanTokenFromCache('idToken');
cleanTokenFromCache(authConfig.clientId);
return true;
}
return true;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/extension-client/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ComposerGlobalName } from '../common/constants';
/**
* log out current user
*/
export function logOut(): Promise<void> {
export function logOut(): Promise<boolean> {
return window[ComposerGlobalName].logOut();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const APPLICATION_KEYS = [
'api.confirm',
'api.updateFlowZoomRate',
'api.telemetryClient',
'api.addNotification',
'api.deleteNotification',
'api.markNotificationAsRead',
'api.hideNotification',
];

export function useApplicationApi(): ApplicationContext & ApplicationContextApi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const builtInStyles = {

const dialog = {
title: {
fontWeight: FontWeights.bold,
fontWeight: FontWeights.semibold,
},
};

Expand Down
27 changes: 24 additions & 3 deletions Composer/packages/server/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@
"add_alternative_phrasing_17e0304c": {
"message": "+ Add alternative phrasing"
},
"add_an_existing_bot_5a9cc5b1": {
"message": "Add an existing bot"
},
"add_app_id_and_password_164e0fdb": {
"message": "Add App ID and Password"
},
Expand Down Expand Up @@ -431,6 +434,12 @@
"azure_functions_5e23be5c": {
"message": "Azure Functions"
},
"azure_functions_required_2a035b48": {
"message": "Azure Functions required"
},
"azure_functions_runtime_not_installed_bc24e100": {
"message": "Azure Functions runtime not installed."
},
"azure_web_app_d834cb4c": {
"message": "Azure Web App"
},
Expand Down Expand Up @@ -698,6 +707,9 @@
"composer_logo_ba2048a0": {
"message": "Composer Logo"
},
"composer_needs_azure_functions_36138382": {
"message": "Composer needs Azure Functions"
},
"composer_needs_net_core_sdk_46e2a8ae": {
"message": "Composer needs .NET Core SDK"
},
Expand Down Expand Up @@ -1910,6 +1922,9 @@
"insert_template_reference_bb33720e": {
"message": "Insert template reference"
},
"install_azure_functions_d607f182": {
"message": "Install Azure Functions"
},
"install_error_a9319839": {
"message": "Install Error"
},
Expand Down Expand Up @@ -2621,6 +2636,9 @@
"onboarding_8407871c": {
"message": "Onboarding"
},
"once_you_publish_your_bot_to_azure_you_will_be_rea_93048067": {
"message": "Once you publish your bot to Azure you will be ready to add connections."
},
"ondialogevents_types_3dc569b5": {
"message": "OnDialogEvents Types"
},
Expand Down Expand Up @@ -2894,6 +2912,9 @@
"publish_to_dev_ops_3b8f5a2f": {
"message": "Publish to Dev Ops"
},
"publish_your_bot_9099e323": {
"message": "Publish your bot"
},
"publish_your_bot_to_azure_and_manage_published_bot_67751ca9": {
"message": "Publish your bot to Azure and manage published bots here."
},
Expand Down Expand Up @@ -3191,9 +3212,6 @@
"save_11a80ec3": {
"message": "Save"
},
"save_as_9e0cf70b": {
"message": "Save as"
},
"save_your_skill_manifest_63bf5f26": {
"message": "Save your skill manifest"
},
Expand Down Expand Up @@ -3833,6 +3851,9 @@
"to_perform_provisioning_and_publishing_actions_com_a2c54389": {
"message": "To perform provisioning and publishing actions, Composer requires access to your Azure and MS Graph accounts. Paste access tokens from the az command line tool using the commands highlighted below."
},
"to_run_this_bot_composer_needs_azure_functions_cor_bbbd0e7": {
"message": "To run this bot, Composer needs Azure Functions Core Tools."
},
"to_run_this_bot_composer_needs_net_core_sdk_d1551038": {
"message": "To run this bot, Composer needs .NET Core SDK."
},
Expand Down
18 changes: 18 additions & 0 deletions Composer/packages/types/src/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ export type DisabledMenuActions = {
reason: string;
};

export type Notification = {
type: 'info' | 'warning' | 'error' | 'pending' | 'success';
title: string;
description?: string;
retentionTime?: number;
link?: {
label: string;
onClick: () => void;
};
read?: boolean;
hidden?: boolean;
onRenderCardContent?: ((props: Notification) => JSX.Element) | React.FC<any>;
};

export type ApplicationContextApi = {
navigateTo: (to: string, opts?: { state?: any; replace?: boolean }) => void;
updateUserSettings: (settings: Partial<UserSettings>) => void;
Expand All @@ -74,6 +88,10 @@ export type ApplicationContextApi = {
confirm: (title: string, subTitle: string, settings?: any) => Promise<boolean>;
updateFlowZoomRate: (currentRate: number) => void;
telemetryClient: TelemetryClient;
addNotification: (notification: Notification) => string;
deleteNotification: (id: string) => void;
markNotificationAsRead: (id: string) => void;
hideNotification: (id: string) => void;
};

export type ApplicationContext = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const LearnMoreLink = styled(Link)`
const CreateActionContent = () => {
return (
<Content>
<Title>{formatMessage('Create new resources')}</Title>
<Summary>
<Text>
{formatMessage(
Expand Down
59 changes: 52 additions & 7 deletions extensions/azurePublish/src/components/azureProvisionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import {
getARMTokenForTenant,
useLocalStorage,
useTelemetryClient,
TelemetryClient,
useApplicationApi,
} from '@bfc/extension-client';
import { Subscription } from '@azure/arm-subscriptions/esm/models';
import { DeployLocation, AzureTenant } from '@botframework-composer/types';
import { FluentTheme, NeutralColors } from '@uifabric/fluent-theme';
import { LoadingSpinner, ProvisionHandoff } from '@bfc/ui-shared';
import { dialogStyle, LoadingSpinner, OpenConfirmModal, ProvisionHandoff } from '@bfc/ui-shared';
import {
ScrollablePane,
ScrollbarVisibility,
Expand Down Expand Up @@ -77,7 +79,6 @@ type ProvisionFormData = {
type ProvisonActionsStylingProps = {
showSignout: boolean;
};

const AddResourcesSectionName = styled(Text)`
font-size: ${FluentTheme.fonts.mediumPlus.fontSize};
`;
Expand Down Expand Up @@ -267,7 +268,18 @@ const getHostname = (config) => {
return config?.environment ? `${config.name}-${config.environment}` : config.name;
}
};

type NotificationType = 'info' | 'warning' | 'error' | 'pending' | 'success';
const getLogoutNotificationSettings = (description: string, type: NotificationType) => {
Comment thread
hatpick marked this conversation as resolved.
Outdated
return {
title: '',
retentionTime: 5000,
description: description,
type,
onRenderCardContent: (props) => (
<div style={{ padding: '0 16px 16px 16px', fontSize: '12px' }}>{props.description}</div>
),
};
};
const getDefaultFormData = (currentProfile, defaults) => {
return {
creationType: defaults.creationType ?? 'create',
Expand Down Expand Up @@ -346,6 +358,7 @@ export const AzureProvisionDialog: React.FC = () => {

const [handoffInstructions, setHandoffInstructions] = useState<string>('');
const [showHandoff, setShowHandoff] = useState<boolean>(false);
const { addNotification } = useApplicationApi();
const updateHandoffInstructions = (resources) => {
const createLuisResource = resources.filter((r) => r.key === 'luisPrediction').length > 0;
const createLuisAuthoringResource = resources.filter((r) => r.key === 'luisAuthoring').length > 0;
Expand Down Expand Up @@ -739,21 +752,53 @@ export const AzureProvisionDialog: React.FC = () => {
closeDialog();
}, [importConfig]);

const signoutAndNotify = useCallback(async () => {
const isSignedOut = await logOut();
if (isSignedOut) {
addNotification(
getLogoutNotificationSettings(formatMessage('You have successfully signed out of Azure'), 'info')
);
closeDialog();
} else {
addNotification(
getLogoutNotificationSettings(
formatMessage(
'There was an error attempting to logout of Azure. To complete logout, you may need to restart Composer.'
),
'error'
)
);
}
}, [addNotification]);

const onRenderSecondaryText = useMemo(
() => (props: IPersonaProps) => {
return (
<div
role="button"
style={{ color: 'blue', cursor: 'pointer' }}
onClick={() => {
closeDialog();
logOut();
onClick={async () => {
const confirmed = await OpenConfirmModal(
formatMessage('Sign out from Azure'),
Comment thread
hatpick marked this conversation as resolved.
Outdated
formatMessage(
'By signing out of Azure, your new publishing profile will be canceled and this dialog will close. Do you want to continue?'
),
{
onRenderContent: (subtitle: string) => <div>{subtitle}</div>,
confirmText: formatMessage('Sign out'),
cancelText: formatMessage('Cancel'),
}
);
if (confirmed) {
await signoutAndNotify();
}
}}
>
{props.secondaryText}
</div>
);
},
[]
[signoutAndNotify]
);

const isNextDisabled = useMemo(() => {
Expand Down