-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(mercury): Report Problem Protocol (#21)
Initial commit for ATL-1776 Aries RFC 0035: Report Problem Protocol 1.0 DIF: Report Problem Protocol 2.0 New module protocol-report_problem Boilerplate code for ReportProblem case classes
- Loading branch information
1 parent
57ed209
commit f5a3711
Showing
7 changed files
with
318 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
mercury/prism-mediator/protocol-report-problem/Report-Problem-Protocol.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Report Problem Protocol 1.0 & 2.0 | ||
|
||
This Protocol is parte of Aries (RFC 0035). | ||
Describes how to report errors and warnings in a powerful, interoperable way. | ||
|
||
- Version 1.0 - see [https://github.com/hyperledger/aries-rfcs/tree/main/features/0035-report-problem] | ||
- Version 2.0: | ||
- see [https://identity.foundation/didcomm-messaging/spec/#problem-reports] | ||
- see [https://didcomm.org/report-problem/2.0/] | ||
|
||
NOTE: In this context never reference to `Error` or `Warning`. Always reference as `Problem`. | ||
|
||
TODO: Support [l10n](https://github.com/hyperledger/aries-rfcs/blob/main/features/0043-l10n/README.md) in the Future. | ||
|
||
## PIURI | ||
|
||
- Version 1.0: | ||
- `https://didcomm.org/report-problem/1.0` | ||
- Version 2.0: | ||
- `https://didcomm.org/report-problem/2.0/problem-report` | ||
|
||
## Notes | ||
|
||
The protocol is one-way, a simple one-step notification protocol: | ||
|
||
## Roles | ||
|
||
- `notifier` - Who sends notification. | ||
- `notified` - Who receive notification. |
89 changes: 89 additions & 0 deletions
89
...roblem/src/main/scala/io/iohk/atala/mercury/protocol/reportproblem/v1/ReportProblem.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package io.iohk.atala.mercury.protocol.reportproblem.v1 | ||
|
||
import io.iohk.atala.mercury.model.Message | ||
import io.iohk.atala.mercury.model.PIURI | ||
|
||
/** ReportProblem | ||
* | ||
* Example: | ||
* @see | ||
* https://github.com/hyperledger/aries-rfcs/tree/main/features/0035-report-problem#the-problem-report-message-type | ||
* | ||
* {{{ | ||
* { | ||
* "@type" : "https://didcomm.org/report-problem/1.0/problem-report", | ||
* "@id" : "an identifier that can be used to discuss this error message", | ||
* "~thread" : "info about the threading context in which the error occurred (if any)", | ||
* "description" : { "en": "localized message", "code": "symbolic-name-for-error" }, | ||
* "problem_items" : [ {"<item descrip>": "value"} ], | ||
* "who_retries" : "enum: you | me | both | none", | ||
* "fix_hint" : { "en": "localized error-instance-specific hint of how to fix issue"}, | ||
* "impact" : "enum: message | thread | connection", | ||
* "where" : "enum: you | me | other - enum: cloud | edge | wire | agency | ..", | ||
* "noticed_time" : "<time>", | ||
* "tracking_uri" : "", | ||
* "escalation_uri" : "" | ||
* } | ||
* }}} | ||
*/ | ||
final case class ReportProblem( | ||
// `@type`: String, | ||
`@id`: Option[String] = None, | ||
`~thread`: Option[String] = None, | ||
description: Description, | ||
problem_items: Option[Map[ItemDescrip, String]] = None, // key/value - TODO does `value` here a String? | ||
who_retries: Option[String] = None, | ||
fix_hint: Option[String] = None, | ||
impact: Option[Impact] = None, | ||
where: Option[Where] = None, | ||
noticed_time: Option[ISO8601UTC] = None, | ||
tracking_uri: Option[URI] = None, | ||
escalation_uri: Option[URI] = None, | ||
) { | ||
// assert(`@type` == "https://didcomm.org/report-problem/1.0/problem-report") // this is something for the parser TODO | ||
def `@type`: PIURI = "https://didcomm.org/report-problem/1.0/problem-report" | ||
} | ||
|
||
object ReportProblem { | ||
def toMessage(obj: ReportProblem): Message = { | ||
// FIXME this doesn't seems to full fit the DIDComm message | ||
Message( | ||
from = ???, | ||
to = ???, | ||
body = ???, | ||
id = obj.`@id`.getOrElse(java.util.UUID.randomUUID.toString()), | ||
piuri = obj.`@type`, | ||
) | ||
} | ||
def fromMessage(msg: Message): ReportProblem = ??? // TODO FIXME | ||
} | ||
|
||
final case class Description( // TODO this will be +- a Map | ||
en: Option[String] = None, | ||
code: String | ||
) | ||
|
||
type ItemDescrip = String | ||
|
||
enum WhoRetries { | ||
case you extends WhoRetries | ||
case me extends WhoRetries | ||
case both extends WhoRetries | ||
case none extends WhoRetries | ||
} | ||
|
||
enum Impact { | ||
case message extends Impact | ||
case thread extends Impact | ||
case connection extends Impact | ||
} | ||
|
||
type Where = String | ||
// enum Where { | ||
// case you extends Where | ||
// case me extends Where | ||
// case other extends Where // FIXME enum: cloud | edge | wire | agency | .. | ||
// } | ||
|
||
type ISO8601UTC = String | ||
type URI = String |
183 changes: 183 additions & 0 deletions
183
...roblem/src/main/scala/io/iohk/atala/mercury/protocol/reportproblem/v2/ReportProblem.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package io.iohk.atala.mercury.protocol.reportproblem.v2 | ||
|
||
import io.iohk.atala.mercury.model.Message | ||
|
||
/** ReportProblem | ||
* | ||
* @see | ||
* https://identity.foundation/didcomm-messaging/spec/#problem-reports | ||
* | ||
* @param pthid | ||
* REQUIRED. The value is the thid of the thread in which the problem occurred. (Thus, the problem report begins a | ||
* new child thread, of which the triggering context is the parent. The parent context can react immediately to the | ||
* problem, or can suspend progress while troubleshooting occurs.) | ||
* | ||
* @param ack | ||
* OPTIONAL. It SHOULD be included if the problem in question was triggered directly by a preceding message. | ||
* (Contrast problems arising from a timeout or a user deciding to cancel a transaction, which can arise independent | ||
* of a preceding message. In such cases, ack MAY still be used, but there is no strong recommendation.) | ||
* | ||
* @param code | ||
* REQUIRED. Deserves a rich explanation; see Problem Codes below. | ||
* | ||
* @param comment | ||
* OPTIONAL but recommended. Contains human-friendly text describing the problem. If the field is present, the text | ||
* MUST be statically associated with code, meaning that each time circumstances trigger a problem with the same | ||
* code, the value of comment will be the same. This enables localization and cached lookups, and it has some | ||
* cybersecurity benefits. The value of comment supports simple interpolation with args (see next), where args are | ||
* referenced as {1}, {2}, and so forth. | ||
* | ||
* @param args | ||
* OPTIONAL. Contains situation-specific values that are interpolated into the value of comment, providing extra | ||
* detail for human readers. Each unique problem code has a definition for the args it takes. In this example, | ||
* e.p.xfer.cant-use-endpoint apparently expects two values in args: the first is a URL and the second is a DID. | ||
* Missing or null args MUST be replaced with a question mark character (?) during interpolation; extra args MUST be | ||
* appended to the main text as comma-separated values. | ||
* | ||
* @param escalate_to | ||
* OPTIONAL. Provides a URI where additional help on the issue can be received. | ||
*/ | ||
final case class ReportProblem( | ||
pthid: String, | ||
ack: Option[Seq[String]], | ||
code: ProblemCode, | ||
comment: Option[String], | ||
args: Option[Seq[String]], | ||
escalate_to: Option[String], | ||
) | ||
object ReportProblem { | ||
|
||
/** {{{ | ||
* { | ||
* "type": "https://didcomm.org/report-problem/2.0/problem-report", | ||
* "id": "7c9de639-c51c-4d60-ab95-103fa613c805", | ||
* "pthid": "1e513ad4-48c9-444e-9e7e-5b8b45c5e325", | ||
* "ack": ["1e513ad4-48c9-444e-9e7e-5b8b45c5e325"], | ||
* "body": { | ||
* "code": "e.p.xfer.cant-use-endpoint", | ||
* "comment": "Unable to use the {1} endpoint for {2}.", | ||
* "args": [ "https://agents.r.us/inbox", "did:sov:C805sNYhMrjHiqZDTUASHg" ], | ||
* "escalate_to": "mailto:[email protected]" | ||
* } | ||
* } | ||
* }}} | ||
* | ||
* @param obj | ||
* @return | ||
*/ | ||
def reportProblemToMessagem(problem: ReportProblem, msg: Message): Message = { | ||
assert(problem.pthid == msg.id) // This is a reply! | ||
Message( | ||
from = msg.to, | ||
to = msg.from, | ||
body = Map( | ||
"code" -> problem.code.value, | ||
"comment" -> problem.comment, | ||
"args" -> problem.args, | ||
"escalate_to" -> problem.escalate_to, | ||
), | ||
ack = problem.ack.getOrElse(Seq.empty), | ||
pthid = Some(problem.pthid) | ||
) | ||
} | ||
} | ||
|
||
/** ProblemCode | ||
* | ||
* @see | ||
* https://identity.foundation/didcomm-messaging/spec/#problem-codes | ||
*/ | ||
|
||
opaque type ProblemCode = String | ||
|
||
object ProblemCode { | ||
def apply(value: String): ProblemCode = { | ||
assert(true) // TODO regex to check value | ||
value | ||
} | ||
} | ||
|
||
extension (problemCode: ProblemCode) { | ||
def sorter: Sorter = problemCode.charAt(0) match | ||
case 'e' => Sorter.e // error semantics | ||
case 'w' => Sorter.w // warning semantics | ||
def scope: Scope = problemCode.charAt(2) match | ||
case 'p' => Scope.p // error semantics | ||
case 'm' => Scope.m // warning semantics | ||
def descriptors: Array[Descriptor] = problemCode.split('.').drop(2) | ||
def value = problemCode | ||
} | ||
|
||
/** @see | ||
* https://identity.foundation/didcomm-messaging/spec/#sorter | ||
* | ||
* @param e: | ||
* This problem clearly defeats the intentions of at least one of the parties. It is therefore an error. A situation | ||
* with error semantics might be that a protocol requires payment, but a payment attempt was rejected. | ||
* | ||
* @param w: | ||
* The consequences of this problem are not obvious to the reporter; evaluating its effects requires judgment from a | ||
* human or from some other party or system. Thus, the message constitutes a warning from the sender’s perspective. A | ||
* situation with warning semantics might be that a sender is only able to encrypt a message for some of the | ||
* recipient’s keyAgreement keys instead of all of them (perhaps due to an imperfect overlap of supported crypto | ||
* types). The sender in such a situation might not know whether the recipient considers this an error. | ||
*/ | ||
enum Sorter { | ||
case e extends Sorter | ||
case w extends Sorter | ||
} | ||
|
||
/** @see | ||
* https://identity.foundation/didcomm-messaging/spec/#scope | ||
* | ||
* @param p: | ||
* The protocol within which the error occurs (and any co-protocols started by and depended on by the protocol) is | ||
* abandoned or reset. In simple two-party request-response protocols, the p reset scope is common and appropriate. | ||
* However, if a protocol is complex and long-lived, the p reset scope may be undesirable. Consider a situation where | ||
* a protocol helps a person apply for college, and the problem code is e.p.payment-failed. With such a p reset | ||
* scope, the entire apply-for-college workflow (collecting letters of recommendation, proving qualifications, | ||
* filling out various forms) is abandoned when the payment fails. The p scope is probably too aggressive for such a | ||
* situation. | ||
* | ||
* @param m: | ||
* The error was triggered by the previous message on the thread; the scope is one message. The outcome is that the | ||
* problematic message is rejected (has no effect). If the protocol is a chess game, and the problem code is | ||
* e.m.invalid-move, then someone’s invalid move is rejected, and it is still their turn. | ||
* | ||
* A formal state name from the sender’s state machine in the active protocol. This means the error represented a | ||
* partial failure of the protocol, but the protocol as a whole is not abandoned. Instead, the sender uses the scope to | ||
* indicate what state it reverts to. If the protocol is one that helps a person apply for college, and the problem | ||
* code is e.get-pay-details.payment-failed, then the sender is saying that, because of the error, it is moving back to | ||
* the get-pay-details state in the larger workflow. | ||
*/ | ||
enum Scope { | ||
case p extends Scope | ||
case m extends Scope | ||
} | ||
|
||
/** Descriptors After the sorter and the scope, problem codes consist of one or more descriptors. These are kebab-case | ||
* tokens separated by the . character, where the semantics get progressively more detailed reading left to right. | ||
* Senders of problem reports SHOULD include at least one descriptor in their problem code, and SHOULD use the most | ||
* specific descriptor they can. Recipients MAY specialize their reactions to problems in a very granular way, or MAY | ||
* examine only a prefix of a problem code. | ||
* | ||
* The following descriptor tokens are defined. They can be used by themselves, or as prefixes to more specific | ||
* descriptors. Additional descriptors — particularly more granular ones — may be defined in individual protocols. | ||
* | ||
* @see | ||
* https://identity.foundation/didcomm-messaging/spec/#descriptors | ||
* | ||
* | Token | Value of comment string | | ||
* |:-------------|:--------------------------------------------------| | ||
* | trust | Failed to achieve required trust. | | ||
* | trust.crypto | Cryptographic operation failed. | | ||
* | xfer | Unable to transport data. | | ||
* | did | DID is unusable. | | ||
* | msg | Bad message. | | ||
* | me | Internal error. | | ||
* | me.res | A required resource is inadequate or unavailable. | | ||
* | req | Circumstances don’t satisfy requirements. | | ||
* | req.time | Failed to satisfy timing constraints. | | ||
* | legal | Failed for legal reasons. | | ||
*/ | ||
type Descriptor = String |