-
Notifications
You must be signed in to change notification settings - Fork 208
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!: Discover Features V2 #991
feat!: Discover Features V2 #991
Conversation
Codecov Report
@@ Coverage Diff @@
## 0.3.0-pre #991 +/- ##
=============================================
+ Coverage 85.94% 88.19% +2.25%
=============================================
Files 615 636 +21
Lines 14763 15036 +273
Branches 2495 2536 +41
=============================================
+ Hits 12688 13261 +573
+ Misses 1940 1677 -263
+ Partials 135 98 -37
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @genaris! I had some very minor notes, not really about the specification but more just code cleanup.
I remember from the last WG call that there would be some breaking changes with this module. Will these be introduced in a separate PR?
packages/core/src/modules/basic-messages/BasicMessagesModule.ts
Outdated
Show resolved
Hide resolved
packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts
Outdated
Show resolved
Hide resolved
packages/core/src/modules/discover-features/protocol/v2/V2DiscoverFeaturesService.ts
Outdated
Show resolved
Hide resolved
packages/core/src/modules/question-answer/QuestionAnswerModule.ts
Outdated
Show resolved
Hide resolved
Thanks for the review @blu3beri! Actually this is an attempt to introduce Discover Features V2 before 0.3.0, present some concepts such as the Feature Registry and, as you mention in one of your comments, figure out how to improve module registration API (and module architecture in general) to leverage registration of all features they add to the framework. I would say that it's currently more a discussion than a PR 😄. During the coding and research over Aries RFCs/DIDComm spec I found these 'features' quite arbitrary and generic, so I'm trying to support the mandatory ones (with some nice typing) while still allowing to define other ones that any custom module may want to add. For the moment I'm registering only protocols but there is room for others such as goal codes, DIDComm version and AIP. Resulting Discover Features module API is a bit confusing because V1 uses a single query while V2 allows multiple queries. Not simple to improve without introducing breaking changes in the API, but for sure in 0.3.0 will be handled better. Anyway I'll keep looking at it and hearing suggestions 🤔 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 this is really great @genaris!! Left some notes, but overall looks really good
private registerFeatures(featureRegistry: FeatureRegistry) { | ||
featureRegistry.register( | ||
new Protocol({ | ||
id: 'https://didcomm.org/basicmessage/1.0', | ||
roles: [BasicMessageRole.Sender, BasicMessageRole.Receiver], | ||
}) | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I really like this!
The one downside is that you now have to manually register the protocol for discovery. Or can we still leverage the registered message types for feature discover and this allows to add extra metadata to it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about when adding the 'combine' method, so we can call FeatureRegistry from Dispatcher and make this registration "automatically". A problem I see with this approach (which is current AFJ approach of disclosing features) is that this will make the other agent think we fully support a protocol even if we just implement a single message from it (something related to #956). So in this sense I feel it will be more accurate if the module registers and states "I fully support this protocol, with these roles". Of course it can be wrong, but at least it will be a human interpretation error.
That being said, probably it will be better to add this Dispatcher approach for current 0.2.x (will be useful also for Discover Features V1) and move exclusively to a module method in 0.3.x as mentioned in other comment. What do you think?
@@ -67,6 +69,8 @@ export class ConnectionsModule { | |||
this.messageSender = messageSender | |||
this.didResolverService = didResolverService | |||
this.registerHandlers(dispatcher) | |||
|
|||
this.registerFeatures(featureRegistry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we call this from within the register method of the module? That way it is more aligned with modules as we'll have them in 0.3.0. In 0.3.0 I would also like to move handler registration to the module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely. I think it should be there in order to enforce feature registration when defining a module (and also the handlers, as you mentioned). As I said in a previous comment, I've done some tries by using the register method (had to add featureRegistry as parameter) but didn´t like so much. Maybe we can improve this module API to add separate methods such as:
- registerDependencies(dependencyManager)
- registerFeatures(featureManager)
- registerHandlers(dispatcher)
packages/core/src/modules/discover-features/DiscoverFeaturesModule.ts
Outdated
Show resolved
Hide resolved
const index = this.features.findIndex((item) => item.type === feature.type && item.id === feature.id) | ||
|
||
if (index > -1) { | ||
this.features[index] = this.features[index].combine(feature) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the combine approach 👍 . Maybe we can add an optional override feature later that will not combine but override
As discussed in today's WG, it will be better to focus on next 0.3.0 release so I'll move the base branch of this PR to 0.3.0-pre. This will help also to not need to worry about breaking changes that will be not so easy to handle with current API. Some features that will be added to this module:
|
9f7d14e
to
7337b00
Compare
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
7337b00
to
8af513c
Compare
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is perfect @genaris! Left some small notes, but overall looks good
@@ -156,6 +163,7 @@ export class Agent extends BaseAgent { | |||
dependencyManager.registerSingleton(TransportService) | |||
dependencyManager.registerSingleton(Dispatcher) | |||
dependencyManager.registerSingleton(EnvelopeService) | |||
dependencyManager.registerSingleton(FeatureRegistry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If feature registry is part of core, I think it should not live in the discover features module.
|
||
import { Protocol } from '../discover-features' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. I think the protocol / feature registry should be part of core so not all modules depend on the discover features module. This way the discover features module just exposes it as a protocol, but the internal registration is in core. What do you think?
describe('BasicMessagesModule', () => { | ||
test('registers dependencies on the dependency manager', () => { | ||
new BasicMessagesModule().register(dependencyManager) | ||
new BasicMessagesModule().register(featureRegistry, dependencyManager) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason you put featureRegistry first? It introduces a breaking change that may not be necessary?
|
||
const outbound = createOutboundMessage(connection, queryMessage) | ||
|
||
const replaySubject = new ReplaySubject<Feature[]>(1) | ||
if (options.awaitDisclosures) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting approach! I think I like it 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💪
private eventEmitter: EventEmitter | ||
private stop$: Subject<boolean> | ||
private agentContext: AgentContext | ||
private serviceMap: ServiceMap<DFSs> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other modules I always expose the config as read only property on the API. This was you can access the config later on (and we may add a way to update certain properties at runtime)
this.options = options ?? {} | ||
} | ||
|
||
public get autoAcceptDiscoverFeatureQueries() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to add an inheritDoc here (as used in other modules) so you get documentation when getting a property from the config
/** | ||
* Get the supported protocol versions based on the provided discover features services. | ||
*/ | ||
export type ProtocolVersionType<DFSs extends DiscoverFeaturesService[]> = DFSs[number]['version'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should be Called DiscoverFeatureApiOptions I think?
* @param options feature queries to perform, protocol version and optional comment string (only used | ||
* in V1 protocol). If awaitDisclosures is set, perform the query synchronously with awaitDisclosuresTimeoutMs timeout. | ||
*/ | ||
public async queryFeatures(options: QueryFeaturesOptions<DFSs>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe too much overhead/complex but do you think we can add return type definition that takes into account the answer to awaitDosclosures?
That way you don't have an optional return type, which can improve DX
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Thanks @TimoGlastra for your re-review 😄 . I think I've addressed all your suggestions (very appropriate BTW). There are of course some things to improve but I think it's ready to merge. |
Can you write a short message describing the breaking changes of this PR? Then I can include those in the commit for the change log |
OK I think that finally it's only about Discover Features V1 module usage so I would say:
|
* feat: expose featureRegistry & add some e2e tests * feat: add synchronous feature query Signed-off-by: Ariel Gentile <gentilester@gmail.com> BREAKING CHANGE: - `queryFeatures` method parameters have been unified to a single `QueryFeaturesOptions` object that requires specification of Discover Features protocol to be used. - `isProtocolSupported` has been replaced by the more general synchronous mode of `queryFeatures`, which works when `awaitDisclosures` in options is set. Instead of returning a boolean, it returns an object with matching features - Custom modules implementing protocols must register them in Feature Registry in order to let them be discovered by other agents (this can be done in module `register(dependencyManager, featureRegistry)` method) Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Discover Features V1 refactor + Initial API implementation of Discover Features V2 with a
FeatureRegistry
where arbitrary features can be registered and queried by modules, and also externally (i.e. to let any controller app setting a custom feature that is willing to disclose).Discover Features module now has a configuration setting to define whether queries should be automatically responded, or leave that decision (and feature set to disclose) to business logic. In either case, events are emitted when receiving queries and disclosures.
Module API is very simple, as we have only two methods whose goal is self-explanatory:
Depending on options, queries can use Discover Features V1 or V2 (maybe we could add an auto-selection depending on the complexity of the query) and can be done either asynchronously or synchronously, meaning that the promise will resolve when the response is received (or a timeout). This is not a common pattern in the rest of the framework, but in this case could be useful to quickly ask for features like it happens in
RecipientModule
to determine mediator pickup strategy.Feature Registry has been exposed in
agent.features
key, in order to let the framework controller to add their own custom features (such as Goal Codes). Probably we should add some checks to prevent adding certain feature types from outside of the framework in order to provide consistent features in disclosures (e.g. we should not allow to register a protocol if there is no module that handles it).Signed-off-by: Ariel Gentile gentilester@gmail.com