Skip to content
This repository has been archived by the owner on Jan 21, 2022. It is now read-only.

ens plugin #111

Merged
merged 6 commits into from
Nov 12, 2018
Merged

ens plugin #111

merged 6 commits into from
Nov 12, 2018

Conversation

StevenJNPearce
Copy link
Contributor

@StevenJNPearce StevenJNPearce commented Oct 3, 2018

Fixes #106

Requirements

  • Enhance the Address scalar to identify .eth names. The parseLiteral and parseValue functions should return a promise, which immediately resolves to the original value if it's not an ENS name, and to the resolved address if it's an ENS name.

  • Perform resolution through this library: https://github.com/ensdomains/ensjs - This library is incompatible with web3-1.0.0 - I've used the 'ez-ens' library, it works great.

  • Solution must be developed as an EthQL plugin called ens. This plugin:

  • * Depends on the 'web3' service, and is ordered after core.

  • * Adds a new service ens: a singleton instance of the ENS object instantiated with the ensjs library above.

  • * Adds resolvers that wrap core resolvers, awaiting on the promise and delegating to the underlying resolver. These fields introduced by the core schema, and resolved there, are to be wrapped:

  • * top-level account

  • * transactionsInvolving

  • * transactionsRoles

Definition of done

  • Test cases with full coverage.
  • PR accepted and merged, introducing a new top-level directory ens with the specified plugin.
  • TODO Code commented, documented and linted.
  • TODO README and wiki updated listing and showcasing this functionality.

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

Some initial feedback to unblock you ;-)

src/__tests__/ens/ens.test.ts Show resolved Hide resolved
@@ -0,0 +1,11 @@
import ENS = require('ez-ens');
Copy link
Contributor

Choose a reason for hiding this comment

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

Putting this file in src/ens/services/ens.ts should be fine. It's simple enough to not need a directory.

@@ -0,0 +1,57 @@
import { GraphQLScalarType, Kind } from 'graphql';
Copy link
Contributor

Choose a reason for hiding this comment

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

Commit 5ba9227 introduced a change that allows the resolver map to be a function, taking in the previous resolvers and returning new ones that will be merged. Using this approach, ens wouldn't need to hard-reference the resolvers from core, it would just assume they are there (given the ordering sequence, as core is applied before), and would wrap around them accordingly. The way I see it, ens resolvers resolve the address promise, and call the original resolver that comes in the prev argument.

}

// tslint:disable-next-line
const Address = new GraphQLScalarType({
Copy link
Contributor

Choose a reason for hiding this comment

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

We should separate the Address scalar type from the rest into different files, e.g. scalars.ts and resolvers.ts (look at how it's done in core).

serialize: String,
parseValue: input => (Web3.utils.isAddress(input) || isEnsDomain(input) ? input : undefined),
parseLiteral: ast => {
if (ast.kind !== Kind.STRING || (!Web3.utils.isAddress(ast.value) && !isEnsDomain(ast.value))) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We actually want to return a Promise<String> in all cases, whether it is an ENS domain or not. If it's an ETH address, we'd return Promise.resolve(ast.value), whereas if it's an ENS domain, we'd return the promise from the ENS lib.

Then the wrapper resolvers (see above) would resolve the promise before delegating onto the original resolver.

Copy link
Contributor Author

@StevenJNPearce StevenJNPearce Oct 6, 2018

Choose a reason for hiding this comment

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

@raulk I'm not sure of how to implement it this way, as context/services are not accessible as they are when declaring a resolver.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right. I'm thinking the scalar parsers can return a 1-arg thunk, e.g.

type AddressThunk = (context: EthqlContext) => Promise<string>;

Then the resolver could do something like:

const addr = await params.address(context);

@@ -60,6 +60,7 @@
"dataloader": "^1.4.0",
"deepmerge": "^2.1.1",
"express": "^4.16.3",
"ez-ens": "^1.0.4",
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious as to why this choice of library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

compact + it works. Seemed like a good option.

@StevenJNPearce
Copy link
Contributor Author

@raulk I was a little busy these last couple of days. I should have plenty of time to work on this tomorrow.

@raulk
Copy link
Contributor

raulk commented Oct 15, 2018

@StevenJNPearce just checking in to see if you've got any follow-up questions?

@raulk raulk mentioned this pull request Oct 15, 2018
9 tasks
@StevenJNPearce
Copy link
Contributor Author

I was just busy, sorry about the wait. Will let you know if I have any questions.

@raulk
Copy link
Contributor

raulk commented Oct 18, 2018

@StevenJNPearce any chance we can get an ETC here? We have other things inflight which depend on the ENS integration now.

@StevenJNPearce
Copy link
Contributor Author

@raulk I took another look at this today and have resolved most of the issues that came up in the review expect one. The one I've not dealt with is #111 (comment) . Whilst I could understand the exmaples in the tests with the commit referenced in your comment, I couldn't get my head round how to implement it - I found the combination of the reduce, merge and functions with (prev, next) arguments a bit confusing.

@raulk
Copy link
Contributor

raulk commented Oct 24, 2018

@StevenJNPearce yeah, that's a tricky part. I'm happy to accept suggestions regarding a clearer API.

The idea here is that some plugins are "decorator" plugins. This is the case of ens. It should not explicitly refer to other resolvers, but it should take any existing ones and "wrap" them in custom logic.

Let me have a look and propose a change on your PR.

import resolvers from './resolvers';
import scalars from './scalars';

export default [resolvers, scalars].reduce((prev, curr) => _.merge({}, curr, prev), {});
Copy link
Contributor

Choose a reason for hiding this comment

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

Take for example the account resolver which now needs to be wrapped in the ENS resolution logic.

Instead of returning a resolvers map here, we would return a function like the following:

export default (prev) => {
  const wrappedResolvers = {
      Query: { 
        account: (obj, args, context: EthqlContext) => {
          args.address = await args.address(context);
          return prev.Query.account(obj, args, context);
        }
      },
      // same for the other affected resolvers
      // Block: { transactionsInvolving, transactionsRoles },
  };

  return _.mergeDeep(prev, wrappedResolvers);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@raulk looks like i forgot to comment back here after fixing this last week. You can check it now.

ens plugin

ens plugin
@StevenJNPearce
Copy link
Contributor Author

@raulk Commenting here as you may have missed my comment on having fixed the last change requested above.

@StevenJNPearce
Copy link
Contributor Author

@raulk I don't see a way to make a pull request against the wiki repository. Maybe you can take the contents of README.md from my last commit and add them to the wiki.

@raulk
Copy link
Contributor

raulk commented Nov 12, 2018

@StevenJNPearce looking, thanks!

@raulk
Copy link
Contributor

raulk commented Nov 12, 2018

@StevenJNPearce Good job here. Unfortunately, this PR did introduce a serious regression: non-ENS addresses stopped working. And there were no test cases covering queries with normal addresses with the ENS plugin, so it went unnoticed. Luckily I noticed this, and fixed it for you in 4f6cd81.

Strangely, the ConsenSys/ethql CircleCI account hasn't picked up this PR. Looking into that, as I'd like to see a green CircleCI before I merge.

@raulk raulk merged commit 9270831 into Consensys:master Nov 12, 2018
kshinn pushed a commit to kshinn/ethql that referenced this pull request Jan 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support resolving ENS names in queries (as a plugin)
2 participants