• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

microsoft / botbuilder-js / 13264809812

11 Feb 2025 02:09PM CUT coverage: 84.524%. Remained the same
13264809812

Pull #4856

github

web-flow
Merge 4e615f951 into 7534989ce
Pull Request #4856: fix: [#4786] Clarify createBotFrameworkAuthenticationFromConfiguration usage in yo templates

8205 of 10860 branches covered (75.55%)

Branch coverage included in aggregate %.

20555 of 23166 relevant lines covered (88.73%)

4123.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.75
/libraries/botframework-connector/src/auth/botFrameworkClientImpl.ts
1
// Copyright (c) Microsoft Corporation.
2
// Licensed under the MIT License.
3

4
import * as z from 'zod';
1✔
5
import { Activity, ChannelAccount, InvokeResponse, RoleTypes } from 'botframework-schema';
1✔
6
import { BotFrameworkClient } from '../skills';
7
import type { ConnectorClientOptions } from '../connectorApi/models';
8
import { ConversationIdHttpHeaderName } from '../conversationConstants';
1✔
9
import { ServiceClientCredentialsFactory } from './serviceClientCredentialsFactory';
10
import { USER_AGENT } from './connectorFactoryImpl';
1✔
11
import { WebResource } from '@azure/core-http';
1✔
12
import { ok } from 'assert';
1✔
13
import axios from 'axios';
1✔
14

15
const botFrameworkClientFetchImpl = (connectorClientOptions: ConnectorClientOptions): typeof fetch => {
1✔
16
    const { http: httpAgent, https: httpsAgent } = connectorClientOptions?.agentSettings ?? {
×
17
        http: undefined,
18
        https: undefined,
19
    };
20
    const axiosInstance = axios.create({
×
21
        httpAgent,
22
        httpsAgent,
23
        validateStatus: (): boolean => true,
×
24
    });
25

26
    return async (input, init?): Promise<Response> => {
×
27
        const url = z.string().parse(input);
×
28
        const { body, headers } = z.object({ body: z.string(), headers: z.record(z.string()).optional() }).parse(init);
×
29

30
        const response = await axiosInstance.post(url, body, {
×
31
            headers,
32
        });
33
        return {
×
34
            status: response.status,
35
            json: () => response.data,
×
36
        } as Response;
37
    };
38
};
39

40
/**
41
 * @internal
42
 * Implementation of [BotFrameworkClient](xref:botframework-connector.BotFrameworkClient).
43
 */
44
export class BotFrameworkClientImpl implements BotFrameworkClient {
1✔
45
    /**
46
     * @param credentialsFactory A [ServiceClientCredentialsFactory](xref:botframework-connector.ServiceClientCredentialsFactory) instance.
47
     * @param loginEndpoint The login url.
48
     * @param botFrameworkClientFetch A custom Fetch implementation to be used in the [BotFrameworkClient](xref:botframework-connector.BotFrameworkClient).
49
     * @param connectorClientOptions  A [ConnectorClientOptions](xref:botframework-connector.ConnectorClientOptions) object.
50
     */
51
    constructor(
52
        private readonly credentialsFactory: ServiceClientCredentialsFactory,
×
53
        private readonly loginEndpoint: string,
×
54
        private readonly botFrameworkClientFetch?: ReturnType<typeof botFrameworkClientFetchImpl>,
×
55
        private readonly connectorClientOptions?: ConnectorClientOptions,
×
56
    ) {
57
        this.botFrameworkClientFetch ??= botFrameworkClientFetchImpl(this.connectorClientOptions);
×
58

59
        ok(typeof this.botFrameworkClientFetch === 'function');
×
60
    }
61

62
    private toJSON() {
63
        // Ignore ConnectorClientOptions, as it could contain Circular Structure behavior.
64
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
        const { connectorClientOptions, ...rest } = this;
×
66
        return rest;
×
67
    }
68

69
    /**
70
     * @template T The type of body in the InvokeResponse.
71
     * @param fromBotId The MicrosoftAppId of the bot sending the activity.
72
     * @param toBotId The MicrosoftAppId of the bot receiving the activity.
73
     * @param toUrl The URL of the bot receiving the activity.
74
     * @param serviceUrl The callback Url for the skill host.
75
     * @param conversationId A conversation ID to use for the conversation with the skill.
76
     * @param activity The Activity to send to forward.
77
     * @returns {Promise<InvokeResponse<T>>} A promise representing the asynchronous operation.
78
     */
79
    async postActivity<T>(
80
        fromBotId: string,
81
        toBotId: string,
82
        toUrl: string,
83
        serviceUrl: string,
84
        conversationId: string,
85
        activity: Activity,
86
    ): Promise<InvokeResponse<T>> {
87
        z.object({
×
88
            fromBotId: z.string().optional(),
89
            toBotId: z.string().optional(),
90
            toUrl: z.string(),
91
            serviceUrl: z.string(),
92
            conversationId: z.string(),
93
            activity: z.record(z.unknown()),
94
        }).parse({
95
            fromBotId,
96
            toBotId,
97
            toUrl,
98
            serviceUrl,
99
            conversationId,
100
            activity,
101
        });
102

103
        const credentials = await this.credentialsFactory.createCredentials(
×
104
            fromBotId,
105
            toBotId,
106
            this.loginEndpoint,
107
            true,
108
        );
109

110
        // Capture current activity settings before changing them.
111
        // TODO: DO we need to set the activity ID? (events that are created manually don't have it).
112
        const originalConversationId = activity.conversation.id;
×
113
        const originalServiceUrl = activity.serviceUrl;
×
114
        const originalRelatesTo = activity.relatesTo;
×
115
        const originalRecipient = activity.recipient;
×
116

117
        try {
×
118
            activity.relatesTo = {
×
119
                serviceUrl: activity.serviceUrl,
120
                activityId: activity.id,
121
                channelId: activity.channelId,
122
                conversation: {
123
                    id: activity.conversation.id,
124
                    name: activity.conversation.name,
125
                    conversationType: activity.conversation.conversationType,
126
                    aadObjectId: activity.conversation.aadObjectId,
127
                    isGroup: activity.conversation.isGroup,
128
                    properties: activity.conversation.properties,
129
                    role: activity.conversation.role,
130
                    tenantId: activity.conversation.tenantId,
131
                },
132
                bot: null,
133
            };
134
            activity.conversation.id = conversationId;
×
135
            activity.serviceUrl = serviceUrl;
×
136

137
            // Fixes: https://github.com/microsoft/botframework-sdk/issues/5785
138
            if (!activity.recipient) {
×
139
                activity.recipient = {} as ChannelAccount;
×
140
            }
141
            activity.recipient.role = RoleTypes.Skill;
×
142

143
            const webRequest = new WebResource(toUrl, 'POST', JSON.stringify(activity), undefined, {
×
144
                Accept: 'application/json',
145
                [ConversationIdHttpHeaderName]: conversationId,
146
                'Content-Type': 'application/json',
147
                'User-Agent': USER_AGENT,
148
            });
149
            const request = await credentials.signRequest(webRequest);
×
150

151
            const config: RequestInit = {
×
152
                body: request.body,
153
                headers: request.headers.rawHeaders(),
154
            };
155
            const response = await this.botFrameworkClientFetch(request.url, config);
×
156

157
            return { status: response.status, body: await response.json() };
×
158
        } finally {
159
            // Restore activity properties.
160
            activity.conversation.id = originalConversationId;
×
161
            activity.serviceUrl = originalServiceUrl;
×
162
            activity.relatesTo = originalRelatesTo;
×
163
            activity.recipient = originalRecipient;
×
164
        }
165
    }
166
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc