Skip to content
Merged
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
28 changes: 21 additions & 7 deletions acapy_agent/core/conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,18 @@ async def setup(self):
)
LOGGER.debug("Inbound transports registered successfully.")

# Register all outbound transports
LOGGER.debug("Setting up outbound transports.")
self.outbound_transport_manager = OutboundTransportManager(
self.root_profile, self.handle_not_delivered
)
# Always register outbound transports (needed for webhook delivery
# even when DIDComm transports are disabled via --no-transport)
LOGGER.debug("Setting up outbound transports.")
self.outbound_transport_manager = OutboundTransportManager(
self.root_profile, self.handle_not_delivered
)
if context.settings.get("transport.disabled"):
# In no-transport mode, register HTTP transport for webhook delivery
self.outbound_transport_manager.register("http")
else:
await self.outbound_transport_manager.setup()
LOGGER.debug("Outbound transports registered successfully.")
LOGGER.debug("Outbound transports registered successfully.")

# Initialize dispatcher
LOGGER.debug("Initializing dispatcher.")
Expand Down Expand Up @@ -335,14 +340,18 @@ async def start(self) -> None:
LOGGER.debug("Wallet type validated.")

if not context.settings.get("transport.disabled"):
# Start up transports if enabled
# Start up inbound transports if enabled
try:
LOGGER.debug("Transport not disabled. Starting inbound transports.")
await self.inbound_transport_manager.start()
LOGGER.debug("Inbound transports started successfully.")
except Exception:
LOGGER.exception("Unable to start inbound transports.")
raise

# Always start outbound transports (needed for webhook delivery
# even when DIDComm transports are disabled via --no-transport)
if self.outbound_transport_manager:
try:
LOGGER.debug("Starting outbound transports.")
await self.outbound_transport_manager.start()
Expand Down Expand Up @@ -865,6 +874,11 @@ def webhook_router(
metadata: Additional metadata associated with the payload

"""
if not self.outbound_transport_manager:
LOGGER.warning(
"Cannot send webhook: outbound transport manager is not available"
)
return
try:
self.outbound_transport_manager.enqueue_webhook(
topic, payload, endpoint, max_attempts, metadata
Expand Down
123 changes: 122 additions & 1 deletion acapy_agent/core/tests/test_conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ async def test_webhook_router(self):

test_topic = "test-topic"
test_payload = {"test": "payload"}
test_endpoint = "http://example"
test_endpoint = "https://example"
test_attempts = 2

test_profile = await create_test_profile(None, await builder.build_context())
Expand Down Expand Up @@ -1449,6 +1449,127 @@ async def test_webhook_router(self):
test_topic, test_payload, test_endpoint, test_attempts, None
)

async def test_webhook_router_no_outbound_manager(self):
builder: ContextBuilder = StubContextBuilder(self.test_settings)
conductor = test_module.Conductor(builder)
conductor.outbound_transport_manager = None

with mock.patch.object(test_module, "LOGGER") as mock_logger:
conductor.webhook_router(
"test-topic", {"test": "payload"}, "https://example", 2
)
mock_logger.warning.assert_called_once()

async def test_setup_no_transport_mode(self):
builder: ContextBuilder = StubContextBuilder(self.test_settings)
builder.update_settings({"transport.disabled": True})
conductor = test_module.Conductor(builder)

test_profile = await create_test_profile(None, await builder.build_context())

with (
mock.patch.object(
test_module,
"wallet_config",
return_value=(
test_profile,
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
),
),
mock.patch.object(
test_module, "OutboundTransportManager", autospec=True
) as mock_outbound_mgr,
):
mock_outbound_mgr.return_value.registered_transports = {
"test": mock.MagicMock(schemes=["http"])
}
await conductor.setup()

# Inbound transports should NOT be set up
assert conductor.inbound_transport_manager is None

# Outbound transport manager should be created
mock_outbound_mgr.assert_called_once()
# HTTP transport should be registered for webhook delivery
mock_outbound_mgr.return_value.register.assert_called_once_with("http")
# setup() should NOT be called (that registers from config)
mock_outbound_mgr.return_value.setup.assert_not_awaited()

async def test_start_no_transport_mode(self):
builder: ContextBuilder = StubContextBuilder(self.test_settings)
builder.update_settings({"transport.disabled": True})
conductor = test_module.Conductor(builder)

test_profile = await create_test_profile(None, await builder.build_context())

with (
mock.patch.object(
test_module,
"wallet_config",
return_value=(
test_profile,
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
),
),
mock.patch.object(
test_module, "OutboundTransportManager", autospec=True
) as mock_outbound_mgr,
):
mock_outbound_mgr.return_value.registered_transports = {
"test": mock.MagicMock(schemes=["http"])
}
await conductor.setup()

mock_outbound_mgr.return_value.registered_transports = {}

await conductor.start()

# Outbound transports should still be started for webhooks
mock_outbound_mgr.return_value.start.assert_awaited_once_with()

await conductor.stop()
mock_outbound_mgr.return_value.stop.assert_awaited_once_with()

async def test_webhook_in_no_transport_mode(self):
builder: ContextBuilder = StubContextBuilder(self.test_settings)
builder.update_settings({"transport.disabled": True})
conductor = test_module.Conductor(builder)

test_topic = "test-topic"
test_payload = {"test": "payload"}
test_endpoint = "https://example"
test_attempts = 2

test_profile = await create_test_profile(None, await builder.build_context())

with (
mock.patch.object(
test_module,
"wallet_config",
return_value=(
test_profile,
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
),
),
mock.patch.object(
test_module, "OutboundTransportManager", autospec=True
) as mock_outbound_mgr,
):
mock_outbound_mgr.return_value.registered_transports = {
"test": mock.MagicMock(schemes=["http"])
}
await conductor.setup()

with mock.patch.object(
conductor.outbound_transport_manager, "enqueue_webhook"
) as mock_enqueue:
conductor.webhook_router(
test_topic, test_payload, test_endpoint, test_attempts
)
mock_enqueue.assert_called_once_with(
test_topic, test_payload, test_endpoint, test_attempts, None
)

async def test_shutdown_multitenant_profiles(self):
builder: ContextBuilder = StubContextBuilder(
{**self.test_settings, "multitenant.enabled": True}
Expand Down
Loading