-
Notifications
You must be signed in to change notification settings - Fork 369
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
Add IdentityMetadata to Contractkit #1126
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1126 +/- ##
======================================
Coverage 66.7% 66.7%
======================================
Files 259 259
Lines 7454 7454
Branches 432 498 +66
======================================
Hits 4972 4972
+ Misses 2386 2384 -2
- Partials 96 98 +2
Continue to review full report at Codecov.
|
@@ -62,6 +62,7 @@ | |||
"@types/node": "^10", | |||
"@types/web3": "^1.0.18", | |||
"globby": "^8", | |||
"prettier": "1.13.5", |
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 believe this shouldn't be necessary since it's already in the root package json.
Ditto for the typescript line just below it. @mcortesi can you confirm? I believe you had a PR to remove these from the package.json files a while back?
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.
that's right, you don't need that. It's on the root pkg
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.
When I ran yarn run docs
, it complained that prettier
was an unknown command, so I had to add it to run 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.
Left some comments
@@ -62,6 +62,7 @@ | |||
"@types/node": "^10", | |||
"@types/web3": "^1.0.18", | |||
"globby": "^8", | |||
"prettier": "1.13.5", |
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.
that's right, you don't need that. It's on the root pkg
@@ -39,7 +42,8 @@ | |||
"@celo/ganache-cli": "git+https://github.com/celo-org/ganache-cli.git#98ad2ba", | |||
"@celo/protocol": "1.0.0", | |||
"@types/debug": "^4.1.5", | |||
"@types/web3": "^1.0.18" | |||
"@types/web3": "^1.0.18", | |||
"ts-node": "8.3.0" |
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 on the root pkg, shouldn't be 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.
When running yarn run ts-node
from packages/contractkit
, my local env complains that ts-node is an unknown command, sounds like the same issue as above?
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 it's a yarn version problem? do a yarn install
on root?
I do it all the time and i don't have that problem
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.
were you abel to fix this after upgrading yarn?
@@ -2,6 +2,8 @@ | |||
"extends": ["@celo/typescript/tslint.json"], | |||
"rules": { | |||
"no-global-arrow-functions": false, | |||
"no-console": false | |||
"no-console": false, |
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 think it's better to disable this by file or by line, not for the whole project.
import { TaskEither, tryCatch } from 'fp-ts/lib/TaskEither' | ||
import { writeFileSync } from 'fs' | ||
|
||
export class NetworkError extends Error {} |
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.
we need to check and have a common way of creating custom erorrs...
I know this is wrong... but i always have to google for it
Check: https://www.google.com/search?q=typescript+subclass+error
export type NameClaim = t.TypeOf<typeof NameClaimType> | ||
export type IdentityMetadata = t.TypeOf<typeof IdentityMetadataType> | ||
|
||
export class ValidationError extends Error {} |
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.
check error subclassing in js/ts.
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, I was not aware. What would you recommend us to do? https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work ?
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.
Yeah it's one of the common js weird things.
The link you've sent has a good recommendation.
Also, I remember doing some kind of createError()
function to define new error classes
|
||
export const parseMetadata = (str: string) => { | ||
const firstPass = parseJSON(str, toError) | ||
return chain(deserializePayloads)(firstPass) |
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.
it seems to me that we are parsingJSON, and then we assume it has some schema.
In particual, we assume data.claims
Isn't io-ts supposed to do the validation for use?
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.
io-ts
can validate a JS object to be of a particular shape, but unless you produce specific types that parse from a string, io-ts
does not by default do the JSON parsing itself. So in practice, we would generally do parseMetadata
in conjunction with validateMetadata
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.
Yes, but you are iterating it (on deserializePayloads), asssuming that (a) is an object, (b) has a claims
property which is an array.
Validating that should be what the validator library does (either io-ts or any other json validator library)
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.
Oh yeah, you are right. Right now this code just assumes an object that has the claims property. It is because the payload itself is serialized twice, which does not fit well into the built-in types (plus eventually verifying the signatures). It seems to me that instead of shoe-horning it into the types, I would actually keep this separate, what do you think?
export type MetadataError = NetworkError | ValidationError | ||
|
||
export class MetadataManager { | ||
emptyIdentityMetadata: IdentityMetadata = { |
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.
should this be readonly? freezed?
return pipe( | ||
data, | ||
findClaim(ClaimTypes.ATTESTATION_SERVICE_URL), | ||
// @ts-ignore Typescript can't infer that the claim must be an Attestation service claim |
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.
it can, the problem is in findClaim ;)
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'm taking suggestions on how to improve findClaim
. I tried, but couldn't figure out an implementation that correctly types the returned claim unfortunately. I maybe naively blame the compiler
) | ||
|
||
findAttestationServiceClaimE = (data: IdentityMetadata): Either<ClaimNotFoundError, string> => { | ||
return pipe( |
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.
to be honest...
the not so fancy fp impl seems easier to read
findAttestationServiceClaimE = (data: IdentityMetadata) => findClaim(ClaimTypes.ATTESTATION_SERVICE_URL, data).url
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.
findClaim
returns an Either
which is why you can't just access .url
packages/cli/src/base.ts
Outdated
@@ -41,6 +43,13 @@ export abstract class BaseCommand extends Command { | |||
return this._kit | |||
} | |||
|
|||
get metadataManager() { |
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 think this should not be accessible from kit, instead have a module elsewhere that you can import from contractKit to use it. Since it's probably not interesting for most of our users.
Same as we did with BlockExplorer
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.
Left a few comments, but merge when you see fit.
it feels much simpler...!
there was an extra file before, right?
} | ||
|
||
addClaim(claim: Claim) { | ||
this.data = { claims: [...this.data.claims, this.signClaim(claim)] } |
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 seems nor one thing, nor the other. We have a mutable object IdentityMetadataWrapper, but we treat this.data
as an immutable one.
You can do:
this.data.claims.push(this.signClaim(claim))
and assume it's mutable and you "own" this.data
or treat everything immutable and then do
addClaim(claim: Claim): IdentityMetadataWrapper {
return new IdentityMetadataWrapper({ claims: [...this.data.claims, this.signClaim(claim)] })
}
assuming of course you can create IdentityMetadataWrapper
with it (which is another suggestion)
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.
Good point!
I'll do the mutation, but will probably leave addClaim
as it is. I don't think chaining claims is going to be that common
break | ||
} | ||
|
||
console.info(`(claim created ${moment.unix(claim.payload.timestamp).fromNow()})\n`) |
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 could be a interesting helper to add to the cli
helpers. relativeDate()
or something
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 think using moment is easy enough for now, but we can re-assess in the future
} | ||
|
||
constructor(data: IdentityMetadata) { | ||
const result = IdentityMetadataType.decode(data) |
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 would argue that this step is for before we call data
an IdentityMetadata
...
if we "trust" the types, then data
is already an IdentityMetadata
.
So, for me, this belongs to the fromRawString
function, where we construct an IdentityMetadata
from a string.
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.
You are right again!
* master: (31 commits) Upgrade to Node 10 (#1148) [faucet] Add custom metrics (#1143) Add IdentityMetadata to Contractkit (#1126) [Wallet] Local currency v1.1 (#1137) Add attestation-service deploy (#1128) [Wallet] A few docs and build cleanup (#1138) [CircleCI]Add comment on how to fix lint checks (#1134) 2019-09-30 integration deployment (#1149) Update web3 provider to new integration url (#1151) [celotool]Add fast mode to celotool invite (#1135) Revert "Feature/909 proxy delegatecall" (#1146) Use contractkit in notification service (#1118) Feature/909 proxy delegatecall (#1003) integration deployment for 2019-09-29 (#1139) Add instructions for npm publication to tag commit (#1117) Client Logs Data Flow script update (#1055) Deploying latest proxy code in genesis (#1122) Enable floating promises check everywhere (fix issues) (#1115) [cli] Solution for build error contractkit on Linux 19.04 distro (#960) [Wallet] Merge back changes made for mx pilot (#1113) ... # Conflicts: # yarn.lock
* master: (35 commits) [Wallet] Network fee in transaction feed (#1145) New About Page Cover (#905) Upgrade to Node 10 (#1148) [faucet] Add custom metrics (#1143) Add IdentityMetadata to Contractkit (#1126) [Wallet] Local currency v1.1 (#1137) Add attestation-service deploy (#1128) [Wallet] A few docs and build cleanup (#1138) [CircleCI]Add comment on how to fix lint checks (#1134) 2019-09-30 integration deployment (#1149) Update web3 provider to new integration url (#1151) [celotool]Add fast mode to celotool invite (#1135) Revert "Feature/909 proxy delegatecall" (#1146) Use contractkit in notification service (#1118) Feature/909 proxy delegatecall (#1003) integration deployment for 2019-09-29 (#1139) Add instructions for npm publication to tag commit (#1117) Client Logs Data Flow script update (#1055) Deploying latest proxy code in genesis (#1122) Enable floating promises check everywhere (fix issues) (#1115) ... # Conflicts: # packages/web/src/about/About.tsx # packages/web/src/about/images/index.ts # packages/web/static/locales/en/about.json
Description
This PR adds the ability for ContractKit consumers to use MetadataManager to fetch metadata as per celo-org/celo-proposals#4, validate it and find claims for an attestation service in the data. It also adds a simple cli command to
do it end-to-end including fetching the metadata URL from the
Attestations
contract.Tested
Other changes
This PR adds the packages
io-ts
andfp-ts
as a first proof-of-concept.io-ts
allows us to specify the type of external data that our code expects and gives us runtime validation of that data. Conventionally, we'd have to write extra functions asserting the shape of the data. Intypes.ts
you can see an example of how I specified the structure of the metadata payload, and then simply usingIdentityMetadata.decode
,io-ts
will do all the heavy lifting for us. I expect this to be also handy when building HTTP servers to assert request payloads (+other use cases)fp-ts
is used byio-ts
and allows us to specify an alternative way to express computation, especially computation that can fail. Instead of errors that are being thrown, caught and then re-thrown by using try-catch everywhere, one can write functions that encode their failure mode into its return value withEither
. Using something likeEither
allows us to compose computation that can fail more easily than conventional errors. (an example)I'm looking for feedback if you think we should use
io-ts
orfp-ts
more broadly. It is clear, that this style of programming is going to be less familiar with most developers and thus is the biggest reason to not adopt these packages. However, in my opinion they come with serious benefits to reliability and composition.Related issues