Skip to content

Commit

Permalink
port: [microsoft#4277] Catch HttpRequestException in HttpRequest (#64…
Browse files Browse the repository at this point in the history
…04) (microsoft#4304)

* HttpRequest class updated. Unit test created.

* Added botbuilder-core dependency.

Co-authored-by: Emiliano Quiroga <emiliano.quiroga@7-11.com>
  • Loading branch information
ceciliaavila and Emiliano Quiroga authored Aug 9, 2022
1 parent 02b7b26 commit c692c81
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ describe('ActionTests', function () {
{ text: 'text', age: 11 },
])
.reply(200, 'array');
nock('http://foo.com')
.post('/', 'Joe is 52')
.replyWithError({ message: 'Error making the request', code: 'FetchError' });
nock('http://foo.com').get('/image').reply(200, 'TestImage');
nock('http://foo.com').get('/json').reply(200, { test: 'test' });
nock('http://foo.com').get('/activity').reply(200, MessageFactory.text('testtest'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@
"$kind": "Microsoft.SendActivity",
"activity": "${turn.lastresult.content}"
},
{
"$kind": "Microsoft.HttpRequest",
"method": "POST",
"url": "http://foo.com/",
"body": "${dialog.name} is ${dialog.age}",
"contentType": "plain/text"
},
{
"$kind": "Microsoft.SendActivity",
"activity": "${turn.lastresult.content}"
},
{
"$kind": "Microsoft.HttpRequest",
"method": "GET",
Expand Down Expand Up @@ -130,6 +141,10 @@
"$kind": "Microsoft.Test.AssertReply",
"text": "array"
},
{
"$kind": "Microsoft.Test.AssertReply",
"text": "request to http://foo.com/ failed, reason: Error making the request"
},
{
"$kind": "Microsoft.Test.AssertReply",
"text": "test"
Expand Down
7 changes: 4 additions & 3 deletions libraries/botbuilder-dialogs-adaptive/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@
}
},
"dependencies": {
"@microsoft/recognizers-text-sequence": "~1.1.4",
"@microsoft/recognizers-text": "~1.1.4",
"@microsoft/recognizers-text-number-with-unit": "~1.1.4",
"@microsoft/recognizers-text-number": "~1.1.4",
"@microsoft/recognizers-text-choice": "~1.1.4",
"@microsoft/recognizers-text-date-time": "~1.1.4",
"@microsoft/recognizers-text-number": "~1.1.4",
"@microsoft/recognizers-text-number-with-unit": "~1.1.4",
"@microsoft/recognizers-text-sequence": "~1.1.4",
"@microsoft/recognizers-text-suite": "1.1.4",
"adaptive-expressions": "4.1.6",
"botbuilder": "4.1.6",
"botbuilder-core": "4.1.6",
"botbuilder-dialogs": "4.1.6",
"botbuilder-dialogs-adaptive-runtime-core": "4.1.6",
"botbuilder-dialogs-declarative": "4.1.6",
Expand Down
152 changes: 89 additions & 63 deletions libraries/botbuilder-dialogs-adaptive/src/actions/httpRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import fetch from 'node-fetch';
import { StatusCodes } from 'botbuilder-core';
import fetch, { FetchError } from 'node-fetch';
import { Activity } from 'botbuilder';
import { BoolProperty, EnumProperty, StringProperty, UnknownProperty } from '../properties';
import { Response, Headers } from 'node-fetch';
Expand Down Expand Up @@ -286,78 +287,103 @@ export class HttpRequest<O extends object = {}> extends Dialog<O> implements Htt
instanceHeaders['Content-Type'] = contentType;

let instanceBody: string;
const body = evaluateExpression(dc.state, this.body);
if (body) {
if (typeof body === 'string') {
instanceBody = body;
} else {
instanceBody = JSON.stringify(Object.assign({}, body));
let traceInfo;
try {
const body = evaluateExpression(dc.state, this.body);
if (body) {
if (typeof body === 'string') {
instanceBody = body;
} else {
instanceBody = JSON.stringify(Object.assign({}, body));
}
}
}

const traceInfo = {
request: {
method: instanceMethod,
url: instanceUrl,
headers: instanceHeaders,
content: instanceBody,
},
response: undefined,
};

let response: Response;

switch (this.method) {
case HttpMethod.DELETE:
case HttpMethod.GET:
response = await fetch(instanceUrl, {
method: instanceMethod,
headers: instanceHeaders,
});
break;
case HttpMethod.PUT:
case HttpMethod.PATCH:
case HttpMethod.POST:
response = await fetch(instanceUrl, {
traceInfo = {
request: {
method: instanceMethod,
url: instanceUrl,
headers: instanceHeaders,
body: instanceBody,
});
break;
}
content: instanceBody,
},
response: undefined,
};

const result = new Result(response.headers);
result.statusCode = response.status;
result.reasonPhrase = response.statusText;

switch (this.responseType.getValue(dc.state)) {
case ResponsesTypes.Activity:
result.content = await response.json();
dc.context.sendActivity(result.content as Activity);
break;
case ResponsesTypes.Activities:
result.content = await response.json();
dc.context.sendActivities(result.content as Activity[]);
break;
case ResponsesTypes.Json: {
const content = await response.text();
try {
result.content = JSON.parse(content);
} catch {
result.content = content;
let response: Response;

switch (this.method) {
case HttpMethod.DELETE:
case HttpMethod.GET:
response = await fetch(instanceUrl, {
method: instanceMethod,
headers: instanceHeaders,
});
break;
case HttpMethod.PUT:
case HttpMethod.PATCH:
case HttpMethod.POST:
response = await fetch(instanceUrl, {
method: instanceMethod,
headers: instanceHeaders,
body: instanceBody,
});
break;
}

const result = new Result(response.headers);
result.statusCode = response.status;
result.reasonPhrase = response.statusText;

switch (this.responseType.getValue(dc.state)) {
case ResponsesTypes.Activity:
result.content = await response.json();
dc.context.sendActivity(result.content as Activity);
break;
case ResponsesTypes.Activities:
result.content = await response.json();
dc.context.sendActivities(result.content as Activity[]);
break;
case ResponsesTypes.Json: {
const content = await response.text();
try {
result.content = JSON.parse(content);
} catch {
result.content = content;
}
break;
}
case ResponsesTypes.Binary: {
const buffer = await response.arrayBuffer();
result.content = new Uint8Array(buffer);
break;
}
break;
case ResponsesTypes.None:
default:
break;
}
case ResponsesTypes.Binary: {
const buffer = await response.arrayBuffer();
result.content = new Uint8Array(buffer);
break;

return await this.endDialogWithResult(dc, result, traceInfo);
} catch (err) {
if (err instanceof FetchError) {
const result = new Result();
result.content = err.message;
result.statusCode = StatusCodes.NOT_FOUND;
return await this.endDialogWithResult(dc, result, traceInfo);
} else {
throw err;
}
case ResponsesTypes.None:
default:
break;
}
}

/**
* Writes Trace Activity for the http request and response values and returns the actionResult as the result of this operation.
*
* @param dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) for the current turn of conversation.
* @param result Value returned from the dialog that was called. The type
* of the value returned is dependent on the child dialog.
* @param traceInfo Trace information to be written.
* @returns A `Promise` representing the asynchronous operation.
*/
private async endDialogWithResult(dc: DialogContext, result: Result, traceInfo: any): Promise<DialogTurnResult> {
traceInfo.response = result;

// Write trace activity for http request and response values.
Expand Down

0 comments on commit c692c81

Please sign in to comment.