-
Notifications
You must be signed in to change notification settings - Fork 516
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
Empty ServiceDecorator in OobRecord Response causes 422 Unprocessable Entity Error #2242
Comments
Thanks for the detail. I’m not sure where this change would be made. Perhaps @ianco or @jcourt562 could provide a pointer? |
I've looked at this again, and I believe I've traced the source of the problem. To recap, the I've looked at all the instances where a async def _service_decorator_from_service(
self, service: Union[Service, str]
) -> ServiceDecorator:
if isinstance(service, str):
(
endpoint,
recipient_keys,
routing_keys,
) = await self.resolve_invitation(service)
return ServiceDecorator(
endpoint=endpoint,
recipient_keys=recipient_keys,
routing_keys=routing_keys,
)
else:
# Create ~service decorator from the oob service
recipient_keys = [
DIDKey.from_did(did_key).public_key_b58
for did_key in service.recipient_keys
]
routing_keys = [
DIDKey.from_did(did_key).public_key_b58
for did_key in service.routing_keys
]
return ServiceDecorator(
endpoint=service.service_endpoint,
recipient_keys=recipient_keys,
routing_keys=routing_keys,
) The else block seems to have the problem. The class Service(Resource):
"""Representation of DID Document Services."""
id: DIDUrl
type: str
service_endpoint: Union[DIDUrl, AnyUrl, Literal[""], List[ServiceEndpoint]] which will always return a string for service_endpoint - at worst an empty string The else block, however, seems that it can potentially introduce nulls into the ServiceDecorator object, because class Service(BaseModel):
...
def __init__(
...
service_endpoint: str = None,
): As an aside, it's a bit of a code smell to cast None to a str ... it should actually be a Optional[str]. Because the compiler indicates it's a string, when its default is actually None. My proposal is that the following should only be attempted if service.service_endpoint is not None: return ServiceDecorator(
endpoint=service.service_endpoint,
recipient_keys=recipient_keys,
routing_keys=routing_keys,
) It seems reasonable to me that if the endpoint is None, then we should return None, instead of a ServiceDecorator. Because the only place the result is used, is to call async def handle_message(
self,
profile: Profile,
messages: List[Dict[str, Any]],
oob_record: OobRecord,
their_service: Optional[ServiceDecorator] = None,
): If their_service has an empty endpoint, then a ServiceDecorator object shouldn't be necessary. I feel it should be the same if the endpoint is an empty string. @dbluhm - it seems you helped with the Service implementation. Crux of the issue is that, at the moment, the OobRecord response made in |
Yes, unfortunately, this is all over ACA-Py. Before the type checking tools really matured to where they are today, it was a common convention (at least in ACA-Py) for Optional values to be expressed as
The empty string I'm curious, how is the connectionless invitation constructed that is resulting in these issues? If there isn't a valid service endpoint to message, it seems like it will ultimately result in an error still since the receiver wouldn't be able to respond to it. I agree that the types in these methods needs to be tightened up and perhaps returning a |
The ACA swagger spec provides the following spec for
OobRecord
:I draw your attention to:
their_service: Optional[ServiceDecorator]
For
ServiceDecorator
we have the following:What you'll notice is that
recipientKeys
andserviceEndpoint
are required when providing aServiceDecorator
.However, when receiving an
OobRecord
response fromreceive_invitation
, we've encountered422 Unprocessable Entity
due to the response providing an emptytheir_service
field.We could not yet intercept the body of the message before parsing into the response object, but the error message indicates:
In a nutshell: we're receiving a
their_service
, but with None for its required fields.This occurs when creating a connectionless invitation in a multitenant environment, and so perhaps this particular case was missed.
A simple workaround for us is to manually make the
ServiceDecorator
fields optional:But of course, this is malforming the
ServiceDecorator
class. It would be more appropriate fortheir_service
to be None, when its required fields are empty.I'd be happy to look into making a PR for this if someone can direct me to the relevant code. Thanks!
The text was updated successfully, but these errors were encountered: