Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@

from pydantic import Field

from .failure import SignInFailureInvokeActivity
from .token_exchange import SignInTokenExchangeInvokeActivity
from .verify_state import SignInVerifyStateInvokeActivity

SignInInvokeActivity = Annotated[
Union[SignInTokenExchangeInvokeActivity, SignInVerifyStateInvokeActivity],
Union[SignInTokenExchangeInvokeActivity, SignInVerifyStateInvokeActivity, SignInFailureInvokeActivity],
Field(discriminator="name"),
]

__all__ = [
"SignInTokenExchangeInvokeActivity",
"SignInVerifyStateInvokeActivity",
"SignInFailureInvokeActivity",
"SignInInvokeActivity",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from typing import Literal

from ....models import SignInFailure
from ...invoke_activity import InvokeActivity


class SignInFailureInvokeActivity(InvokeActivity):
"""
Sign-in failure invoke activity for signin/failure invokes.

Represents an invoke activity when a sign-in operation fails
during the authentication process.
"""

name: Literal["signin/failure"] = "signin/failure"
"""The name of the operation associated with an invoke or event activity."""

value: SignInFailure
"""A value that is associated with the activity."""
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from .card import SignInCard
from .exchange_token import SignInExchangeToken
from .failure import SignInFailure
from .response import SignInUrlResponse
from .state_verify_query import SignInStateVerifyQuery

__all__ = ["SignInUrlResponse", "SignInCard", "SignInExchangeToken", "SignInStateVerifyQuery"]
__all__ = ["SignInUrlResponse", "SignInCard", "SignInExchangeToken", "SignInStateVerifyQuery", "SignInFailure"]
26 changes: 26 additions & 0 deletions packages/api/src/microsoft/teams/api/models/sign_in/failure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from typing import Optional

from ..custom_base_model import CustomBaseModel


class SignInFailure(CustomBaseModel):
"""
Sign-in failure information.

Represents the details of a sign-in failure including
an error code and message.
"""

code: Optional[str] = None
"""
The error code for the sign-in failure
"""
message: Optional[str] = None
"""
The error message for the sign-in failure
"""
76 changes: 76 additions & 0 deletions packages/api/tests/unit/test_signin_failure_activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

import pytest
from microsoft.teams.api.activities.invoke.sign_in import SignInFailureInvokeActivity
from microsoft.teams.api.models import Account, ConversationAccount, SignInFailure


@pytest.mark.unit
class TestSignInFailureInvokeActivity:
"""Unit tests for SignInFailureInvokeActivity class."""

def test_should_create_signin_failure_activity(self) -> None:
"""Test creating a signin failure activity with code and message."""
failure = SignInFailure(code="resourcematchfailed", message="Resource match failed")
user = Account(id="user-1", name="Test User")
bot = Account(id="bot-1", name="Test Bot")
conversation = ConversationAccount(id="conv-1")

activity = SignInFailureInvokeActivity(
id="activity-1",
name="signin/failure",
value=failure,
from_=user,
conversation=conversation,
recipient=bot,
)

assert activity.name == "signin/failure"
assert activity.type == "invoke"
assert activity.value.code == "resourcematchfailed"
assert activity.value.message == "Resource match failed"

def test_should_deserialize_signin_failure_activity(self) -> None:
"""Test deserializing a signin failure activity from JSON-like dict."""
activity_dict = {
"id": "activity-1",
"name": "signin/failure",
"type": "invoke",
"from": {"id": "user-1", "name": "Test User"},
"conversation": {"id": "conv-1"},
"recipient": {"id": "bot-1", "name": "Test Bot"},
"value": {"code": "resourcematchfailed", "message": "Resource match failed"},
}

activity = SignInFailureInvokeActivity.model_validate(activity_dict)

assert activity.name == "signin/failure"
assert activity.type == "invoke"
assert activity.value.code == "resourcematchfailed"
assert activity.value.message == "Resource match failed"

def test_should_serialize_signin_failure_activity(self) -> None:
"""Test serializing a signin failure activity to dict."""
failure = SignInFailure(code="unauthorized", message="Unauthorized access")
user = Account(id="user-1", name="Test User")
bot = Account(id="bot-1", name="Test Bot")
conversation = ConversationAccount(id="conv-1")

activity = SignInFailureInvokeActivity(
id="activity-1",
name="signin/failure",
value=failure,
from_=user,
conversation=conversation,
recipient=bot,
)

serialized = activity.model_dump()

assert serialized["name"] == "signin/failure"
assert serialized["type"] == "invoke"
assert serialized["value"]["code"] == "unauthorized"
assert serialized["value"]["message"] == "Unauthorized access"
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
P = TypeVar("P", bound=BaseModel)


@Plugin(
name="mcp-server", version=version, description="MCP server plugin that exposes AI functions as MCP tools"
)
@Plugin(name="mcp-server", version=version, description="MCP server plugin that exposes AI functions as MCP tools")
class McpServerPlugin(PluginBase):
"""
MCP Server Plugin for Teams Apps.
Expand Down