Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add timeout to did resolver resolve method #2464

Merged
merged 2 commits into from
Sep 6, 2023
Merged
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
32 changes: 22 additions & 10 deletions aries_cloudagent/resolver/did_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
retrieving did's from different sources provided by the method type.
"""

import asyncio
from datetime import datetime
from itertools import chain
import logging
Expand All @@ -29,6 +30,8 @@
class DIDResolver:
"""did resolver singleton."""

DEFAULT_TIMEOUT = 30

def __init__(self, resolvers: Optional[List[BaseDIDResolver]] = None):
"""Create DID Resolver."""
self.resolvers = resolvers or []
Expand All @@ -42,20 +45,28 @@ async def _resolve(
profile: Profile,
did: Union[str, DID],
service_accept: Optional[Sequence[Text]] = None,
*,
timeout: Optional[int] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not initialize to 30 instead of None? That would make it more evident that the time-out default is 30 seconds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair question; using the optional enables expression that the timeout wasn't set. Since we call _resolve from two other methods, and we want the timeout to apply to both and we want a default of 30 seconds when the timeout isn't set, we would have to set the default timeout to 30 on both the resolve and resolve_with_metadata methods or else make sure the timeout wasn't included in the call to _resolve. It ends up being cleaner to set the default when timeout is None. But I think there's a middle ground. I'll put a constant on the class that has the default timeout value so it's clearer what it is and how it's used at a glance.

) -> Tuple[BaseDIDResolver, dict]:
"""Retrieve doc and return with resolver."""
# TODO Cache results
"""Retrieve doc and return with resolver.

This private method enables the public resolve and resolve_with_metadata
methods to share the same logic.
"""
if isinstance(did, DID):
did = str(did)
else:
DID.validate(did)
for resolver in await self._match_did_to_resolver(profile, did):
try:
LOGGER.debug("Resolving DID %s with %s", did, resolver)
document = await resolver.resolve(
profile,
did,
service_accept,
document = await asyncio.wait_for(
resolver.resolve(
profile,
did,
service_accept,
),
timeout if timeout is not None else self.DEFAULT_TIMEOUT,
)
return resolver, document
except DIDNotFound:
Expand All @@ -68,18 +79,20 @@ async def resolve(
profile: Profile,
did: Union[str, DID],
service_accept: Optional[Sequence[Text]] = None,
*,
timeout: Optional[int] = None,
) -> dict:
"""Resolve a DID."""
_, doc = await self._resolve(profile, did, service_accept)
_, doc = await self._resolve(profile, did, service_accept, timeout=timeout)
return doc

async def resolve_with_metadata(
self, profile: Profile, did: Union[str, DID]
self, profile: Profile, did: Union[str, DID], *, timeout: Optional[int] = None
) -> ResolutionResult:
"""Resolve a DID and return the ResolutionResult."""
resolution_start_time = datetime.utcnow()

resolver, doc = await self._resolve(profile, did)
resolver, doc = await self._resolve(profile, did, timeout=timeout)

time_now = datetime.utcnow()
duration = int((time_now - resolution_start_time).total_seconds() * 1000)
Expand Down Expand Up @@ -120,7 +133,6 @@ async def dereference(
document: Optional[BaseDIDDocument] = None,
) -> Resource:
"""Dereference a DID URL to its corresponding DID Doc object."""
# TODO Use cached DID Docs when possible
try:
parsed = DIDUrl.parse(did_url)
if not parsed.did:
Expand Down