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

microsoft / botbuilder-js / 13290160003

12 Feb 2025 04:29PM CUT coverage: 84.47% (-0.05%) from 84.524%
13290160003

Pull #4834

github

web-flow
Merge 56cc7a45a into 7534989ce
Pull Request #4834: refactor: [#4759] Migrate off @azure/core-http

8260 of 10940 branches covered (75.5%)

Branch coverage included in aggregate %.

129 of 148 new or added lines in 22 files covered. (87.16%)

6 existing lines in 3 files now uncovered.

20572 of 23193 relevant lines covered (88.7%)

3968.88 hits per line

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

16.13
/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 { createWebResource } from 'botbuilder-stdlib/lib/azureCoreHttpCompat';
1✔
12
import { createHttpHeaders } from '@azure/core-rest-pipeline';
1✔
13
import { ok } from 'assert';
1✔
14
import axios from 'axios';
1✔
15

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

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

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

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

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

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

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

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

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

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

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

NEW
144
            const webRequest = createWebResource({
×
145
                url: toUrl,
146
                method: 'POST',
147
                body: JSON.stringify(activity),
148
                headers: createHttpHeaders({
149
                    Accept: 'application/json',
150
                    [ConversationIdHttpHeaderName]: conversationId,
151
                    'Content-Type': 'application/json',
152
                    'User-Agent': USER_AGENT,
153
                }),
154
            });
155
            const request = await credentials.signRequest(webRequest);
×
156

157
            const config: RequestInit = {
×
158
                body: request.body,
159
                headers: request.headers.rawHeaders(),
160
            };
161
            const response = await this.botFrameworkClientFetch(request.url, config);
×
162

163
            return { status: response.status, body: await response.json() };
×
164
        } finally {
165
            // Restore activity properties.
166
            activity.conversation.id = originalConversationId;
×
167
            activity.serviceUrl = originalServiceUrl;
×
168
            activity.relatesTo = originalRelatesTo;
×
169
            activity.recipient = originalRecipient;
×
170
        }
171
    }
172
}
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