private async internalSendActivityRequestAsync()

in source/nodejs/adaptivecards/src/adaptive-applet.ts [521:715]


    private async internalSendActivityRequestAsync(request: ActivityRequest) {
        if (!this.channelAdapter) {
            throw new Error("internalSendActivityRequestAsync: channelAdapter is not set.");
        }

        const overlay = this.createProgressOverlay(request);

        if (overlay !== undefined) {
            this.renderedElement.appendChild(overlay);
        }

        let done = false;

        while (!done) {
            let response: ActivityResponse | undefined = undefined;

            if (request.attemptNumber === 1) {
                logEvent(
                    Enums.LogLevel.Info,
                    "Sending activity request to channel (attempt " + request.attemptNumber + ")"
                );
            } else {
                logEvent(
                    Enums.LogLevel.Info,
                    "Re-sending activity request to channel (attempt " + request.attemptNumber + ")"
                );
            }

            try {
                response = await this.channelAdapter.sendRequestAsync(request);
            } catch (error) {
                logEvent(Enums.LogLevel.Error, "Activity request failed: " + error);

                this.removeProgressOverlay(request);

                done = true;
            }

            if (response) {
                if (response instanceof SuccessResponse) {
                    this.removeProgressOverlay(request);

                    if (response.rawContent === undefined) {
                        throw new Error(
                            "internalSendActivityRequestAsync: Action.Execute result is undefined"
                        );
                    }

                    let parsedContent = response.rawContent;

                    try {
                        parsedContent = JSON.parse(response.rawContent);
                    } catch {
                        // Leave parseContent as is
                    }

                    if (typeof parsedContent === "string") {
                        logEvent(
                            Enums.LogLevel.Info,
                            "The activity request returned a string after " +
                                request.attemptNumber +
                                " attempt(s)."
                        );

                        this.activityRequestSucceeded(response, parsedContent);
                    } else if (
                        typeof parsedContent === "object" &&
                        parsedContent["type"] === "AdaptiveCard"
                    ) {
                        logEvent(
                            Enums.LogLevel.Info,
                            "The activity request returned an Adaptive Card after " +
                                request.attemptNumber +
                                " attempt(s)."
                        );

                        this.internalSetCard(parsedContent, request.consecutiveRefreshes);
                        this.activityRequestSucceeded(response, this.card);
                    } else {
                        throw new Error(
                            "internalSendActivityRequestAsync: Action.Execute result is of unsupported type (" +
                                typeof response.rawContent +
                                ")"
                        );
                    }

                    done = true;
                } else if (response instanceof ErrorResponse) {
                    const retryIn: number = this.activityRequestFailed(response);

                    if (
                        retryIn >= 0 &&
                        request.attemptNumber < GlobalSettings.applets.maximumRetryAttempts
                    ) {
                        logEvent(
                            Enums.LogLevel.Warning,
                            `Activity request failed: ${response.error.message}. Retrying in ${retryIn}ms`
                        );

                        request.attemptNumber++;

                        await new Promise<void>((resolve, _reject) => {
                            window.setTimeout(() => {
                                resolve();
                            }, retryIn);
                        });
                    } else {
                        logEvent(
                            Enums.LogLevel.Error,
                            `Activity request failed: ${response.error.message}. Giving up after ${request.attemptNumber} attempt(s)`
                        );

                        this.removeProgressOverlay(request);

                        done = true;
                    }
                } else if (response instanceof LoginRequestResponse) {
                    logEvent(
                        Enums.LogLevel.Info,
                        "The activity request returned a LoginRequestResponse after " +
                            request.attemptNumber +
                            " attempt(s)."
                    );

                    if (request.attemptNumber <= GlobalSettings.applets.maximumRetryAttempts) {
                        let attemptOAuth = true;

                        if (response.tokenExchangeResource && this.onSSOTokenNeeded) {
                            // Attempt to use SSO. The host will return true if it can handle SSO, in which case
                            // we bypass OAuth
                            attemptOAuth = !this.onSSOTokenNeeded(
                                this,
                                request,
                                response.tokenExchangeResource
                            );
                        }

                        if (attemptOAuth) {
                            // Attempt to use OAuth
                            this.removeProgressOverlay(request);

                            if (response.signinButton === undefined) {
                                throw new Error(
                                    "internalSendActivityRequestAsync: the login request doesn't contain a valid signin URL."
                                );
                            }

                            logEvent(
                                Enums.LogLevel.Info,
                                "Login required at " + response.signinButton.value
                            );

                            if (this.onShowSigninPrompt) {
                                // Bypass the built-in auth prompt if the host app handles it
                                this.onShowSigninPrompt(this, request, response.signinButton);
                            } else {
                                this.showAuthCodeInputDialog(request);

                                const left =
                                    window.screenX +
                                    (window.outerWidth - GlobalSettings.applets.authPromptWidth) /
                                        2;
                                const top =
                                    window.screenY +
                                    (window.outerHeight - GlobalSettings.applets.authPromptHeight) /
                                        2;

                                window.open(
                                    response.signinButton.value,
                                    response.signinButton.title
                                        ? response.signinButton.title
                                        : "Sign in",
                                    `width=${GlobalSettings.applets.authPromptWidth},height=${GlobalSettings.applets.authPromptHeight},left=${left},top=${top}`
                                );
                            }
                        }
                    } else {
                        logEvent(
                            Enums.LogLevel.Error,
                            "Authentication failed. Giving up after " +
                                request.attemptNumber +
                                " attempt(s)"
                        );

                        alert(Strings.magicCodeInputCard.authenticationFailed());
                    }

                    // Exit the loop. After a LoginRequestResponse, the host app is responsible for retrying the request
                    break;
                } else {
                    throw new Error("Unhandled response type: " + JSON.stringify(response));
                }
            }
        }
    }