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

refactor!: add agent context #881

Conversation

TimoGlastra
Copy link
Contributor

@TimoGlastra TimoGlastra commented Jun 19, 2022

This is a big pull request, but doesn't change any functionality of the agent. All services, repositories etc.. now use agent context so we can start using them as stateless services for multitenancy (as described here: https://hackmd.io/vGLVlxLvQR6jsEEjzNcL8g#Agent-Context).

This doesn't change the public API of the framework, but because of the large number of changes and also the implications for custom modules I've marked this as a breaking change.

Will add more detailed migration docs later on, but generally you have to update your modules to now inject the agent context object and pass that around to services. See the DummyModule for an example: https://github.com/TimoGlastra/aries-framework-javascript/blob/fb26a4dfe656b38da984ed46faf5b5a5f3cda1df/samples/extension-module/dummy/DummyModule.ts

@TimoGlastra TimoGlastra added the multitenancy Tasks related to multi-tenancy label Jun 19, 2022
@TimoGlastra TimoGlastra requested a review from a team as a code owner June 19, 2022 10:02
@codecov-commenter
Copy link

codecov-commenter commented Jun 19, 2022

Codecov Report

Merging #881 (2be8776) into main (3c84265) will increase coverage by 0.13%.
The diff coverage is 87.74%.

@@            Coverage Diff             @@
##             main     #881      +/-   ##
==========================================
+ Coverage   87.65%   87.78%   +0.13%     
==========================================
  Files         470      473       +3     
  Lines       11172    11251      +79     
  Branches     1868     1905      +37     
==========================================
+ Hits         9793     9877      +84     
+ Misses       1313     1309       -4     
+ Partials       66       65       -1     
Impacted Files Coverage Δ
packages/core/src/agent/AgentConfig.ts 94.64% <ø> (-0.28%) ⬇️
packages/core/src/constants.ts 100.00% <ø> (ø)
...les/credentials/formats/CredentialFormatService.ts 100.00% <ø> (ø)
...cation/handlers/V1RevocationNotificationHandler.ts 71.42% <ø> (ø)
...cation/handlers/V2RevocationNotificationHandler.ts 71.42% <ø> (ø)
...ges/core/src/modules/credentials/services/index.ts 100.00% <ø> (ø)
...ore/src/modules/dids/methods/web/WebDidResolver.ts 60.00% <ø> (ø)
...c/modules/routing/handlers/MediationDenyHandler.ts 62.50% <0.00%> (+6.94%) ⬆️
...ckages/core/src/transport/HttpOutboundTransport.ts 9.75% <0.00%> (-1.88%) ⬇️
packages/core/src/transport/WsOutboundTransport.ts 9.23% <0.00%> (-2.37%) ⬇️
... and 95 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3c84265...2be8776. Read the comment docs.

Copy link
Contributor

@berendsliedrecht berendsliedrecht left a comment

Choose a reason for hiding this comment

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

I have reviewed the first 36 files and will continue somewhere tomorrow (it is quite a bit).

But I do have a comment and that is about the function signatures. I believe we can make every function signature way more consistent by having a common pattern.

Seeing that most functions now take an AgentContext it would nice to have something like this:

const afjFunc = (agentContext: AgentContext, parameters: {...}, options?: {...}) => { ... }


// or 
const afjFunc = (parameters: {...}, options?: {...}) => { ... }

With this we will always have named parameters and have a nice division between the parameters of a function and the configuration/options we supply to it (like shouldDeleteCredential or timeOut). I am interested to hear what you think about this structure and whether it is something we want to implement in AFJ. I think it has value, but it does require a big breaking change.

I am not too sure if we want to also add this to the public api, might be worth discussing.

packages/core/src/agent/MessageSender.ts Outdated Show resolved Hide resolved
this.messageSender = messageSender
this.eventEmitter = eventEmitter
this.logger = agentConfig.logger
this.logger = logger
Copy link
Contributor

Choose a reason for hiding this comment

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

I am a bit confused by this. Maybe I am missing something about the context, but could we not get the logger from agentContext.logger?

Checking a bit through this PR it seems a bit "messy" on how we deal with the logger instance. We get it via the injection but it is also in the agentConfig. I am not too knowledgeable on dependency injection with tsyringe, but is this really the best way to deal with the logger instance?

Also, do we want each agent to have their instance of a logger or do we want to use a RootContainer (if I understand your design correctly) logger instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's something I also wasn't sure about, so good that you're calling it out.

It's in the agent config because we configure it in the agent init config. However my thought was that the logger would be shared between the different tenants. I'm not sure if it would work well if we have a lot of logger instances, and there's ways to different context of the different agents in logs.

As mentioned in the design document we should probably look at a way to separate agent config (global, used by all tenants) and tenant config (specific to that tenant).

What would be your preferred approach here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I think a global configuration, maybe a Provider/manager agent aswell, and tenant specific configuration would be a good solution.

Quickly looking through the AgentConfig I do not see a lot that should be globally defined. That being said, I still think it has value for future configuration.

@@ -1,6 +1,7 @@
// reflect-metadata used for class-transformer + class-validator
import 'reflect-metadata'

export { AgentContext } from './agent/AgentContext'
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we want to expose the AgentContext publicly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we want to inject it (in extension modules for example) we need it. So it's not necessary for the public api for users, but is needed if you want to write extension modules.

@@ -164,7 +166,7 @@ export class DidExchangeProtocol {
)
}

const didDocument = await this.extractDidDocument(message)
const didDocument = await this.extractDidDocument(messageContext.agentContext, message)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why exactly is the agentContext inside the messageContext? Just curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because each message can have their own context. Handlers, services and repositories are now all stateless. The agent context gives the context to process the message. So if we receive a message we'll find the correct tenant, assign the agent context for that tenant to the message context and then process the message.

We could make it a separate parameter, but it kinda made sense to me to tie the agent context to a specific message context. Would you prefer to make it a separate parameter?

Copy link
Contributor

Choose a reason for hiding this comment

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

Its a difficult one. I think for consistency a seperate agentContext might be nicer, but I see your point of containing the agentContext inside the messageContext. I am okay with keeping it like this.

}

public async findSingleByQuery(query: { did: string; theirDid: string }) {
return this.connectionRepository.findSingleByQuery(query)
public async findByDids(agentContext: AgentContext, query: { ourDid: string; theirDid: string }) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason why we call it ourDid? I believe ACA-Py uses my_did? I have no preference, more just curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure, @jakubkoci added this

@TimoGlastra
Copy link
Contributor Author

TimoGlastra commented Jun 19, 2022

Seeing that most functions now take an AgentContext it would nice to have something like this:

const afjFunc = (agentContext: AgentContext, parameters: {...}, options?: {...}) => { ... }


// or 
const afjFunc = (parameters: {...}, options?: {...}) => { ... }

Not 100% sure what you mean by this. This is a pattern already used by some methods, do you mean we should enforce it for all methods? How would we do something like that?

I'm also not sure it will work for all methods. It can be difficult sometimes to differentiate between what parameters and what options are (in the case of the shouldDeleteCredential it's clear, but I think there's a lot of methods where it's not that clear. I'm also not sure if it will improve the API for all methods. E.g. this looks cleaner to me:

agent.credentials.offerCredential({
  connectionId: 'the-connection-id',
  autoAcceptCredential: AutoAcceptCredential.Always,
  credentialFormats: {
  /* formats */
  }
})

than this:

agent.credentials.offerCredential({
  connectionId: 'the-connection-id',
  credentialFormats: {
  /* formats */
  }
}, {
  autoAcceptCredential: AutoAcceptCredential.Always
})

Anyway it seems this would be better addressed to me in a separate issue, except if this involves moving the AgentContext around. I made it the first parameter to each method now for consistency, would you change that?

@berendsliedrecht
Copy link
Contributor

Anyway it seems this would be better addressed to me in a separate issue, except if this involves moving the AgentContext around. I made it the first parameter to each method now for consistency, would you change that?

No the agentContext as first param is a good choice. My point is to get more consistency within the framework. TBH, I do think that the second method with the object for autoAccept is better. And 100% it will be difficult, but I think that finding a clear division between parameters and config/options might cleanup the functions aswell.

Copy link
Contributor

@berendsliedrecht berendsliedrecht left a comment

Choose a reason for hiding this comment

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

Reviewed this time until file: 91. Will continue later today :)

packages/core/src/modules/ledger/IndyPool.ts Show resolved Hide resolved
Copy link
Contributor

@berendsliedrecht berendsliedrecht left a comment

Choose a reason for hiding this comment

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

Great work @TimoGlastra! I do think that we should not mark this as refactor as there are many many changes to functions and the way we use them.

I left a couple of notes but nothing blocking.

We should also merge it probably after the 0.2.0 release.

packages/core/tests/mocks/MockWallet.ts Show resolved Hide resolved
@TimoGlastra TimoGlastra force-pushed the refactor/add-agent-context branch 4 times, most recently from 2be8776 to fb26a4d Compare June 30, 2022 13:22
@TimoGlastra TimoGlastra added the breaking change PR that introduces a breaking change label Jun 30, 2022
Signed-off-by: Timo Glastra <[email protected]>
@TimoGlastra TimoGlastra force-pushed the refactor/add-agent-context branch from fb26a4d to afca7dc Compare July 3, 2022 09:51
@TimoGlastra
Copy link
Contributor Author

Superseded by #920 (which is based on 0.3.0-pre instead of main)

@TimoGlastra TimoGlastra closed this Jul 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change PR that introduces a breaking change multitenancy Tasks related to multi-tenancy
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants