Skip to content

Commit 95cf692

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/dot-github/actions/conventional-pr/actions/core-1.9.1
2 parents 0b142e6 + 8628464 commit 95cf692

File tree

78 files changed

+2703
-1856
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+2703
-1856
lines changed

.config/tsaoptions.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"azurePath": "Composer/Compliance",
3+
"notificationAliases": "[email protected]",
4+
"codebaseName": "TSABotFrameworkComposer",
5+
"ignoreBranchName": true
6+
}

Composer/packages/adaptive-flow/src/adaptive-flow-editor/AdaptiveFlowEditor.tsx

+12-20
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,6 @@ const VisualDesigner: React.FC<VisualDesignerProps> = ({ onFocus, onBlur, schema
137137
editorEvent && handleEditorEvent(editorEvent.type, editorEvent.payload);
138138
};
139139

140-
const marqueeStyles = (_) => {
141-
return {
142-
root: {
143-
width: '100%',
144-
height: '100%',
145-
},
146-
};
147-
};
148140
return (
149141
<CacheProvider value={emotionCache}>
150142
<NodeRendererContext.Provider value={nodeContext}>
@@ -158,15 +150,15 @@ const VisualDesigner: React.FC<VisualDesignerProps> = ({ onFocus, onBlur, schema
158150
{...enableKeyboardCommandAttributes(handleCommand)}
159151
data-testid="visualdesigner-container"
160152
>
161-
<SelectionContext.Provider value={selectionContext}>
162-
<MarqueeSelection isDraggingConstrainedToRoot selection={selection} styles={marqueeStyles}>
163-
<FlowToolbar
164-
flowCommentsVisible={flowCommentsVisible}
165-
flowZoomRate={flowZoomRate}
166-
focusedId={focusedId}
167-
toggleFlowComments={toggleFlowComments}
168-
updateFlowZoomRate={updateFlowZoomRate}
169-
>
153+
<FlowToolbar
154+
flowCommentsVisible={flowCommentsVisible}
155+
flowZoomRate={flowZoomRate}
156+
focusedId={focusedId}
157+
toggleFlowComments={toggleFlowComments}
158+
updateFlowZoomRate={updateFlowZoomRate}
159+
>
160+
<SelectionContext.Provider value={selectionContext}>
161+
<MarqueeSelection isDraggingConstrainedToRoot selection={selection}>
170162
<div
171163
className="flow-editor-container"
172164
css={{
@@ -200,9 +192,9 @@ const VisualDesigner: React.FC<VisualDesignerProps> = ({ onFocus, onBlur, schema
200192
}}
201193
/>
202194
</div>
203-
</FlowToolbar>
204-
</MarqueeSelection>
205-
</SelectionContext.Provider>
195+
</MarqueeSelection>
196+
</SelectionContext.Provider>
197+
</FlowToolbar>
206198
</div>
207199
</SelfHostContext.Provider>
208200
</NodeRendererContext.Provider>

Composer/packages/client/src/recoilModel/dispatchers/__tests__/mocks/mockProjectResponse.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3085,7 +3085,7 @@
30853085
"title": "speak"
30863086
},
30873087
"inputHint": {
3088-
"description": "Indicates whether your bot is accepting,\nexpecting, or ignoring user input after the message is delivered to the client. Possible\nvalues include: 'acceptingInput', 'ignoringInput', 'expectingInput'",
3088+
"description": "Indicates whether your bot is accepting,\nexpecting, or ignoring user input after the message is delivered to the client. Possible\nvalues include: 'acceptingInput', 'ignoringInput', 'expectingInput', 'ignoringSpeechInput', 'ignoringNonSpeechInput'",
30893089
"type": "string",
30903090
"title": "inputHint"
30913091
},

Composer/packages/client/src/recoilModel/dispatchers/publisher.ts

-6
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,6 @@ export const publisherDispatcher = () => {
102102
const { set, snapshot } = callbackHelpers;
103103
const { endpointURL, status, port } = data;
104104

105-
// remove job id in publish storage if published
106-
if (status === PUBLISH_SUCCESS || status === PUBLISH_FAILED) {
107-
const publishJobIds = publishStorage.get('jobIds') || {};
108-
delete publishJobIds[`${projectId}-${target.name}`];
109-
publishStorage.set('jobIds', publishJobIds);
110-
}
111105
// the action below only applies to when a bot is being started using the "start bot" button
112106
// a check should be added to this that ensures this ONLY applies to the "default" profile.
113107
if (target.name === defaultPublishConfig.name) {
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
const { ipcRenderer } = require('electron'); // eslint-disable-line
4+
const { ipcRenderer, contextBridge } = require('electron'); // eslint-disable-line
55

66
// expose ipcRenderer to the browser
7-
window.ipcRenderer = ipcRenderer;
7+
contextBridge.exposeInMainWorld('ipcRenderer', {
8+
send: (...args) => ipcRenderer.send(...args),
9+
on: (...args) => ipcRenderer.on(...args),
10+
});
11+
812
// flag to distinguish electron client from web app client
9-
window.__IS_ELECTRON__ = true;
13+
contextBridge.exposeInMainWorld('__IS_ELECTRON__', true);

Composer/packages/integration-tests/cypress/integration/LuisDeploy.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ context('Luis Deploy', () => {
3636
response: 'fixture:luPublish/success',
3737
});
3838
cy.findByTestId('startBotButton').click();
39-
cy.findByText(/^Starting bot../);
39+
cy.findAllByText(/^Starting bot../);
4040
cy.route('GET', '/api/publish/*/status/default', { endpointURL: 'anything', status: 200 });
4141
cy.route('PUT', '/api/projects/*/files/appsettings.json', { status: 200 });
4242
});

Composer/packages/integration-tests/cypress/support/commands.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55

66
declare namespace Cypress {
77
interface Chainable {
8+
/**
9+
* Get CSRF token
10+
* @example cy.getCSRFToken().then(token => ...)
11+
*/
12+
getCSRFToken(): Chainable<string>;
813
/**
914
* Creates a bot based on empty bot.
1015
* @example cy.createBot('EmptySample', ()=> {})

Composer/packages/integration-tests/cypress/support/commands.ts

+44
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,49 @@ import '@testing-library/cypress/add-commands';
55

66
let TemplateBotProjectId = '';
77

8+
let csrfToken: string;
9+
10+
Cypress.Commands.add('getCSRFToken', () => {
11+
if (csrfToken) {
12+
return cy.wrap(csrfToken);
13+
}
14+
cy.visit('/');
15+
return cy
16+
.window()
17+
.its('__csrf__')
18+
.then((csrf) => {
19+
csrfToken = csrf;
20+
return csrf;
21+
});
22+
});
23+
24+
Cypress.Commands.overwrite('request', (originalFn, ...options) => {
25+
return cy.getCSRFToken().then((csrf) => {
26+
const headers = {
27+
'X-CSRF-Token': csrf,
28+
};
29+
30+
const optionsObject = options[0];
31+
32+
if (optionsObject === Object(optionsObject)) {
33+
optionsObject.headers = {
34+
...headers,
35+
...optionsObject.headers,
36+
};
37+
38+
return originalFn(optionsObject);
39+
}
40+
41+
const [method, url, body] = options;
42+
return originalFn({
43+
method,
44+
url,
45+
body,
46+
headers,
47+
});
48+
});
49+
});
50+
851
Cypress.Commands.add('createBot', (botName: string, callback?: (bot: any) => void) => {
952
const params = {
1053
description: '',
@@ -60,6 +103,7 @@ Cypress.Commands.add('createTestBot', (botName: string, callback?: (bot: any) =>
60103
storageId: 'default',
61104
};
62105

106+
cy.wrap(TemplateBotProjectId).should('not.be.empty');
63107
cy.request('post', `/api/projects/${TemplateBotProjectId}/project/saveAs`, params).then((res) => {
64108
callback?.(res.body);
65109
});

Composer/packages/integration-tests/cypress/support/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import './commands';
55

66
before(() => {
77
cy.exec('yarn clean-all');
8+
cy.getCSRFToken();
89
cy.createTemplateBot('EmptySample');
910
});
1011

Composer/packages/intellisense/src/hooks/useLanguageServer.ts

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export const useLanguageServer = (
5252
ws.current.onmessage = (messageText) => {
5353
handleMessage(messageText);
5454
};
55+
56+
return () => {
57+
ws.current.close();
58+
};
5559
}, [url]);
5660

5761
// If scopes change, update backend with info

Composer/packages/lib/code-editor/src/BaseEditor.tsx

+12-8
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,24 @@ const BaseEditor: React.FC<BaseEditorProps> = (props) => {
170170

171171
const onEditorMount: EditorDidMount = (getValue, editor) => {
172172
editorRef.current = editor;
173+
const action = editorRef.current.addAction({
174+
id: 'escape',
175+
label: 'Escape editor',
176+
keybindings: [MonacoKeyCode.Escape],
177+
keybindingContext: null,
178+
contextMenuGroupId: 'navigation',
179+
contextMenuOrder: 1.5,
180+
run: () => {
181+
(document.activeElement as HTMLElement)?.blur();
182+
},
183+
});
173184

174185
if (typeof editorDidMount === 'function') {
175186
editorDidMount(getValue, editor);
176187
}
177188

178189
editor.onDidDispose(() => {
190+
action.dispose();
179191
editorRef.current = undefined;
180192
});
181193
};
@@ -220,14 +232,6 @@ const BaseEditor: React.FC<BaseEditorProps> = (props) => {
220232
}
221233
}, [onChange, editorRef.current]);
222234

223-
// Add a command to escape from editor
224-
useEffect(() => {
225-
if (!editorRef.current) return;
226-
editorRef.current.addCommand(MonacoKeyCode.Escape, () => {
227-
(document.activeElement as HTMLElement)?.blur();
228-
});
229-
}, [editorRef.current]);
230-
231235
const errorMsgFromDiagnostics = useMemo(() => {
232236
const errors = findErrors(diagnostics);
233237
return errors.length ? combineSimpleMessage(errors) : '';

Composer/packages/lib/code-editor/src/LuEditor.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ const LuEditor: React.FC<LULSPEditorProps> = (props) => {
193193

194194
const uri = get(editor.getModel(), 'uri._formatted', '');
195195

196+
let webSocket: WebSocket;
196197
if (!window.monacoLUEditorInstance) {
197198
const url = createUrl(luServer);
198-
const webSocket: WebSocket = createWebSocket(url);
199+
webSocket = createWebSocket(url);
199200
listen({
200201
webSocket,
201202
onConnection: (connection: MessageConnection) => {
@@ -242,6 +243,10 @@ const LuEditor: React.FC<LULSPEditorProps> = (props) => {
242243
}
243244
sendRequestWithRetry(languageClient, 'initializeDocuments', { luOption, uri });
244245
}
246+
247+
return () => {
248+
webSocket?.close();
249+
};
245250
}, [editor, onNavigateToLuPage]);
246251

247252
const onInit: OnInit = (monaco) => {

Composer/packages/lib/code-editor/src/lg/LgCodeEditor.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ export const LgCodeEditor = (props: LgCodeEditorProps) => {
133133
}
134134

135135
const uri = get(editor.getModel(), 'uri._formatted', '');
136+
let webSocket: WebSocket;
136137

137138
if (!window.monacoLGEditorInstance) {
138139
const url = createUrl(lgServer);
139-
const webSocket: WebSocket = createWebSocket(url);
140+
webSocket = createWebSocket(url);
140141
listen({
141142
webSocket,
142143
onConnection: (connection: MessageConnection) => {
@@ -172,6 +173,10 @@ export const LgCodeEditor = (props: LgCodeEditorProps) => {
172173
})
173174
);
174175
}
176+
177+
return () => {
178+
webSocket?.close();
179+
};
175180
}, [editor, onNavigateToLgPage]);
176181

177182
const onInit: OnInit = (monaco) => {

Composer/packages/lib/code-editor/src/lg/modalityEditors/SpeechModalityEditor.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ const SpeechModalityEditor = React.memo(
7979
text: formatMessage('Expecting'),
8080
selected: inputHint === 'expectingInput',
8181
},
82+
{
83+
key: 'ignoringSpeechInput',
84+
text: formatMessage('Ignoring Speech'),
85+
selected: inputHint === 'ignoringSpeechInput',
86+
},
87+
{
88+
key: 'ignoringNonSpeechInput',
89+
text: formatMessage('Ignoring Non Speech'),
90+
selected: inputHint === 'ignoringNonSpeechInput',
91+
},
8292
],
8393
[inputHint]
8494
);

Composer/packages/lib/code-editor/src/lg/types.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ export type CommonModalityEditorProps = {
3434
/**
3535
* Structured response types.
3636
*/
37-
export const acceptedInputHintValues = ['expectingInput', 'ignoringInput', 'acceptingInput'] as const;
37+
export const acceptedInputHintValues = [
38+
'expectingInput',
39+
'ignoringInput',
40+
'acceptingInput',
41+
'ignoringSpeechInput',
42+
'ignoringNonSpeechInput',
43+
] as const;
3844
export const acceptedAttachmentLayout = ['carousel', 'list'] as const;
3945

4046
export const modalityTypes = ['Text', 'Speak', 'Attachments', 'SuggestedActions'] as const;

Composer/packages/lib/indexers/src/validations/expressionValidation/index.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ export const validateExpressions: ValidateFunc = (
4949
let errorMessage = '';
5050
let warningMessage = '';
5151
try {
52-
newCache[value] = cache?.[value] ? cache[value] : checkExpression(value, required, types);
53-
errorMessage = checkReturnType(newCache[value], types);
52+
const valueToValidate = cache?.[path] ? cache[path] : checkExpression(value, required, types);
53+
errorMessage = checkReturnType(valueToValidate, types);
54+
if (!errorMessage) {
55+
//First validate that the types of the value match and then store the type value in newCache using the path as key to avoid overwriting.
56+
newCache[path] = valueToValidate;
57+
}
5458
} catch (error) {
5559
//change the missing custom function error to warning
5660
warningMessage = filterCustomFunctionError(error.message, customFunctions);

Composer/packages/server/src/directline/middleware/__tests__/attachmentHandler.test.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ const mockSend = jest.fn(() => ({
2020
end: mockEnd,
2121
}));
2222

23+
const mockType = jest.fn(() => ({
24+
send: mockSend,
25+
}));
26+
2327
const mockStatus = jest.fn(() => ({
2428
json: mockJsonResponse,
2529
send: mockSend,
30+
type: mockType,
2631
}));
2732

2833
describe('getAttachment handler', () => {
@@ -54,12 +59,15 @@ describe('getAttachment handler', () => {
5459
end: jest.fn(),
5560
send: jest.fn(),
5661
type: jest.fn(),
62+
status: mockStatus,
5763
json: mockJsonResponse,
5864
};
5965

6066
getAttachmentHandler(req, res);
6167

62-
expect(res.send).toHaveBeenCalledWith(StatusCodes.OK, Buffer.from(mockAttachmentData));
68+
expect(res.status).toHaveBeenCalledWith(StatusCodes.OK);
69+
expect(res.status().type).toHaveBeenCalledWith('text/plain');
70+
expect(res.status().type().send).toHaveBeenCalledWith(Buffer.from(mockAttachmentData));
6371
});
6472

6573
it('should return the attachment content uploaded via the bot', () => {
@@ -80,12 +88,14 @@ describe('getAttachment handler', () => {
8088
end: jest.fn(),
8189
type: jest.fn(),
8290
send: jest.fn(),
83-
status: jest.fn(),
91+
status: mockStatus,
8492
};
8593

8694
getAttachmentHandler(req, res);
8795

88-
expect(res.send).toHaveBeenCalledWith(StatusCodes.OK, Buffer.from(mockAttachmentData.toString(), 'base64'));
96+
expect(res.status).toHaveBeenCalledWith(StatusCodes.OK);
97+
expect(res.status().type).toHaveBeenCalledWith('text/plain');
98+
expect(res.status().type().send).toHaveBeenCalledWith(Buffer.from(mockAttachmentData.toString(), 'base64'));
8999
});
90100

91101
it('should send an error message if the original view is requested, but missing', () => {

Composer/packages/server/src/directline/middleware/attachmentHandler.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ export function createGetAttachmentHandler(state: DLServerState) {
7676
buffer = Buffer.from(attachmentBase64.toString(), 'base64');
7777
}
7878

79-
res.type(attachment.type);
80-
res.send(StatusCodes.OK, buffer);
79+
res.status(StatusCodes.OK).type(attachment.type).send(buffer);
8180
} else {
8281
handleDirectLineErrors(req, res, {
8382
status: StatusCodes.NOT_FOUND,

0 commit comments

Comments
 (0)