Feat: Add IPFS fallback logic to fetch metadata#89
Conversation
Evalir
left a comment
There was a problem hiding this comment.
❤️ Asking some questions!
| } | ||
|
|
||
| this.address = data.address | ||
| this.contentUri = data.contentUri ?? undefined |
There was a problem hiding this comment.
ES2020 🤩 wouldn't it be better to return null?
There was a problem hiding this comment.
Thank for the suggestions. I think in this particular case the problem is that we are implementing the RepositoryData interface and the contentUri has those types. Regardless I agree there is something a bit odd here, and I believe it's a consequence of allowing data on The Graph to be undefined / null.
Anyways I like to know why you think null would be better than undefined?
There was a problem hiding this comment.
It's probably due to my mental model regarding null & undefined; for me, null is the proper thing to use when there's no value. It indicates the absence of it. undefined, for me, indicates problems with fetching some data, a function that returns nothing (void) or some other anomalous development in processing data.
There was a problem hiding this comment.
Agreed on the mental model, I see null as “empty”, while undefined is “nothing”. I also like to keep using the same type when it makes sense, e.g. here we could use "", or if it was a list we could use []: it removes the need to treat null / undefined as an exception for the consumer.
…I wish JS only had null 😆
There was a problem hiding this comment.
Now that you mention about using an empty object of the particular type. I was wondering if we should do that at the subgraph level and always asure that at least we have an empty object. At least for the most part of the attributes.
There was a problem hiding this comment.
Now that you mention about using an empty object of the particular type. I was wondering if we should do that at the subgraph level and always asure that at least we have an empty object. At least for the most part of the attributes.
I think it could be nice yes. 👍
Codecov Report
@@ Coverage Diff @@
## master #89 +/- ##
==========================================
- Coverage 28.82% 28.12% -0.71%
==========================================
Files 53 54 +1
Lines 836 889 +53
Branches 137 145 +8
==========================================
+ Hits 241 250 +9
- Misses 595 639 +44
Continue to review full report at Codecov.
|
bpierre
left a comment
There was a problem hiding this comment.
I left some comments, let me know what you think!
| abi?: Abi | ||
| appName?: string | ||
| author?: string | ||
| contractPath?: string | ||
| deprecatedIntents?: { [version: string]: AppIntent[] } | ||
| description?: string | ||
| icons?: { src: string; sizes: string }[] | ||
| intents?: AppIntent[] | ||
| htmlUrl?: string | ||
| sourceUrl?: string |
There was a problem hiding this comment.
Ah I didn’t think about that… I’m wondering if it’s worth using readonly at all then?
There was a problem hiding this comment.
If we are going to use a method _init() / create() always required probably not worth it. I can't think other alternatives to try to have readonly otherwise.
| const { | ||
| appName, | ||
| path, | ||
| functions, | ||
| deprecatedFunctions, | ||
| abi, | ||
| }: AragonArtifact = await resolveMetadata( | ||
| 'artifact.json', | ||
| this.contentUri!, | ||
| this.#artifact | ||
| ) | ||
|
|
||
| const { | ||
| author, | ||
| description, | ||
| start_url: htmlUrl, | ||
| icons, | ||
| source_url: sourceUrl, | ||
| }: AragonManifest = await resolveMetadata( | ||
| 'manifest.json', | ||
| this.contentUri!, | ||
| this.#manifest | ||
| ) |
There was a problem hiding this comment.
We should probably try / catch these two, and emit contextual errors.
There was a problem hiding this comment.
I decide to handle it in the resolveMetadata function. What you think?
|
As we discussed with Pierre I move the logic of |
|
Note I split the PRs to keep the changes more clear. |
I was thinking about this (moreso in the context of a CLI/frontend using connect to connect to generic apps that may or may not be hosted by our default IPFS node), and I think it's important to provide a way to override the default IPFS gateway used. |
| return this.#created | ||
| } | ||
|
|
||
| async create({ artifact, manifest, ...data }: AppData): Promise<void> { |
There was a problem hiding this comment.
For create(), I was more thinking of a static method, that would fetch everything needed and instantiate, so we can do this:
const app = await App.create(/* … */)Rather than this:
const app = new App(/* … */)
await app.create()That way, App would never be in a state where it is instantiated, but not initialized.
There was a problem hiding this comment.
Oh, I didn't think about that. I'll include this change as a new PR.
| this.htmlUrl = htmlUrl | ||
| this.kernelAddress = data.kernelAddress | ||
| this.name = data.name | ||
| this.registry = data.registry || undefined |
There was a problem hiding this comment.
| this.registry = data.registry || undefined | |
| this.registry = data.registry |
Co-authored-by: Pierre Bertet <hello@pierre.world>
This PR includes a new async method
create()for the entities that need metadata (App, Repo, and Role). This a requirement when creating the entity. We handle this during creation at the connector.If we can't get the metadata from The Graph we use ethers to fetch with a GET request constructing the URL from the
contentUripointing to the default Aragon IPFS node.