Skip to content

ipfs@0.41 update: option 2 #385

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

Merged
merged 9 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"highlight.js": "^9.12.0",
"ipfs": "^0.41.0",
"ipfs-css": "^0.6.0",
"it-all": "^1.0.1",
"it-concat": "^1.0.0",
"meta-marked": "^0.4.2",
"monaco-editor": "^0.6.1",
"new-github-issue-url": "^0.2.1",
Expand Down
7 changes: 4 additions & 3 deletions src/components/Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
import Vue from 'vue'
import CID from 'cids'
import pTimeout from 'p-timeout'
import all from 'it-all'
import newGithubIssueUrl from 'new-github-issue-url'

import { getTutorialByUrl, isTutorialPassed, getLesson } from '../utils/tutorials'
Expand Down Expand Up @@ -410,11 +411,11 @@ export default {
},
createFile: function (ipfs) {
/* eslint-disable no-new */
return ipfs.add(this.ipfsConstructor.Buffer.from('You did it!'))
return all(ipfs.add(this.ipfsConstructor.Buffer.from('You did it!')))
},
createTree: function (ipfs) {
/* eslint-disable no-new */
return ipfs.add([
return all(ipfs.add([
{
content: this.ipfsConstructor.Buffer.from('¯\\_(ツ)_/¯'),
path: 'shrug.txt'
Expand All @@ -427,7 +428,7 @@ export default {
content: this.ipfsConstructor.Buffer.from('You did it!'),
path: 'fun/success.txt'
}
], { wrapWithDirectory: true })
], { wrapWithDirectory: true }))
},
resetCode: function () {
// TRACK? User chose to reset code
Expand Down
10 changes: 6 additions & 4 deletions src/tutorials/0004-mutable-file-system/02.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import utils from '../utils'

const validate = async (result, ipfs) => {
const correctHash = 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn'

if (!result) {
return { fail: 'Oops! You forgot to return a result :(' }
} else if (!!result & !result.hash) {
} else if (!!result & !result.cid) {
return { fail: "That result doesn't look right. Are you sure you ran the `stat` method on your empty root directory?" }
} else if (!!result && result.hash === correctHash) {
} else if (!!result && result.cid.toString() === correctHash) {
return {
success: 'Success! You did it!',
logDesc: "Here's the status of your root directory ( `/` ). Notice that it has a hash (CID) even though it doesn't have contents yet. Every empty IPFS node has this exact same hash, because their non-existent contents are identical!",
log: result
logDesc: "Here's the status of your root directory ( `/` ). Notice that it has a CID even though it doesn't have contents yet. Every empty IPFS node has this exact same CID, because their non-existent contents are identical!",
log: utils.format.ipfsObject(result)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/tutorials/0004-mutable-file-system/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ await ipfs.files.stat('/stuff')

This method returns an object with some essential data about our file or directory:

* **hash** (a string with the cryptographic hash)
* **cid** (the CID object)
* **size** (an integer with the file or directory size in Bytes)
* **cumulativeSize** (an integer with the size of the DAGNodes making up the file in Bytes)
* **type** (a string that can be either `directory` or `file`)
Expand Down
20 changes: 14 additions & 6 deletions src/tutorials/0005-regular-files-api/03.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import all from 'it-all'

import utils from '../utils'

const validate = async (result, ipfs) => {
const uploadedFiles = window.uploadedFiles || false

const expectedResult = await ipfs.add(window.uploadedFiles)
const expectedResult = await all(ipfs.add(window.uploadedFiles))

if (!result) {
return {
Expand Down Expand Up @@ -38,33 +42,37 @@ const validate = async (result, ipfs) => {
if (JSON.stringify(expectedResult) === JSON.stringify(result)) {
return {
success: 'Success! You did it!',
logDesc: 'Your `add` command returned the array of objects below. Notice in particular the `hash` ' + valueText + ", since we'll need " + thatText + ' to access ' + fileText + ' again later. The `path` matches the `hash` for ' + fileText + ", but we'll see in future lessons that that's not always true.",
log: result
logDesc: 'Your `add` command returned the array of objects below. Notice in particular the `cid` ' + valueText + ", since we'll need " + thatText + ' to access ' + fileText + ' again later. The `path` matches the `cid` for ' + fileText + ", but we'll see in future lessons that that's not always true.",
log: result.map(utils.format.ipfsObject)
}
} else {
return { fail: `Something seems to be wrong. Please click "Reset Code" and try again, taking another look at the instructions and editing only the portion of code indicated. Feeling really stuck? You can click "View Solution" to see our suggested code.` }
}
}

const code = `/* global ipfs */
const all = require('it-all')

const run = async (files) => {
const result = // Place your code to add a file or files here
const result = await all() // Place your code to add a file or files here

return result
}
return run
`

const solution = `/* global ipfs */
const all = require('it-all')

const run = async (files) => {
const result = await ipfs.add(files)
const result = await all(ipfs.add(files))

return result
}
return run
`

const modules = { cids: require('cids') }
const modules = { 'it-all': require('it-all') }

const options = {
overrideErrors: true
Expand Down
39 changes: 29 additions & 10 deletions src/tutorials/0005-regular-files-api/03.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
---

## Working with files in ProtoSchool
Here in our ProtoSchool tutorials, we create a new IPFS node for you in the browser each time you hit the "Submit" button in a lesson. Whenever you see `ipfs.someMethod()` in our lessons, `ipfs` is a variable that refers to your IPFS instance, also known as a node. This IPFS node is not carried over from one lesson to another, which is why you'll see that we often prepopulate some code in an exercise to get your new IPFS node to the state you got a different IPFS node to in a previous one.
Here in our ProtoSchool tutorials, we create a new IPFS node for you in the browser each time you hit the "Submit" button in a lesson. Whenever you see `ipfs.someMethod()` in our lessons, `ipfs` is a variable that refers to your IPFS instance, also known as a node. This IPFS node is not carried over from one lesson to another, which is why you'll see that we often pre-populate some code in an exercise to get your new IPFS node to the state you got a different IPFS node to in a previous one.

We create these IPFS nodes behind the scenes so that you can focus on the content of each lesson. Outside of ProtoSchool, though, you'd want a more consistent experience and a node (or multiple nodes) that you could access repeatedly. To acheive this, you could either initialize [JS IPFS in the browser](https://github.com/ipfs/js-ipfs#use-in-the-browser) on your own, or host your own node locally by installing IPFS and running a daemon in your terminal. When you're ready to experiment with that, you can visit the IPFS docs site to learn how to [install IPFS](https://docs.ipfs.io/guides/guides/install/) and [initialize your node](https://docs.ipfs.io/introduction/usage/#initialize-the-repository).
We create these IPFS nodes behind the scenes so that you can focus on the content of each lesson. Outside of ProtoSchool, though, you'd want a more consistent experience and a node (or multiple nodes) that you could access repeatedly. To achieve this, you could either initialize [JS IPFS in the browser](https://github.com/ipfs/js-ipfs#use-in-the-browser) on your own, or host your own node locally by installing IPFS and running a daemon in your terminal. When you're ready to experiment with that, you can visit the IPFS docs site to learn how to [install IPFS](https://docs.ipfs.io/guides/guides/install/) and [initialize your node](https://docs.ipfs.io/introduction/usage/#initialize-the-repository).

As mentioned previously, the methods discussed in this tutorial are part of the IPFS [Files API](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md). Check the documentation for more specific details, such as options for each API method.

Expand All @@ -19,33 +19,52 @@ When you add a file to IPFS, you're putting it only in your own node, but making
Let's take a look at how to add a file your IPFS node. We'll do this by executing the `add` method:

```javascript
await ipfs.add(data, [options])
ipfs.add(data, [options])
```

So if we had the `File` object for an adorable photo of a kitten in our browser, accessible to us via a variable `catPic`, and we wanted to add it to our IPFS node, we could pass it into the `add` method as `data` like so:

```javascript
await ipfs.add(catPic)
ipfs.add(catPic)
```

Note that the `add` method can accept either a single `File` or an array, just in case you have more than one adorable animal photo to add to the node:

```javascript
await ipfs.add([catPic, dogPic, giraffePic])
ipfs.add([catPic, dogPic, giraffePic])
```

Because the `add` method returns a `Promise`, you'll need to place an `await` before the method call to suspend the execution until the return value of the promise is ready to be used.
Because the `add` method returns an `Async Iterable`, you'll need to call the iterable multiple times until you have all the results.

The result of this `Promise` is an array of objects, one for each file added to IPFS, in the following format:
For this, we can use a `for await...of` loop:

```javascript
const result = []

for await (const file of ipfs.add([catPic, dogPic, giraffePic])) {
result.push(file)
}
```

To make things easier, we can use the `it-all` package that does this automatically:

```javascript
const all = require('it-all')

const result = await all(ipfs.add([catPic, dogPic, giraffePic]))
```

The value of the variable `result` is an array of objects, one for each file added to IPFS, in the following format:

```javascript
{
path: string,
hash: string,
size: number
cid: CID,
size: number,
mode: number
}
```

The value of the `hash` is a CID (Content Identifier), a unique address generated from the content of the node. (For a more in-depth look at how CIDs are generated and why they're important, check out our [Decentralized data structures](https://proto.school/#/data-structures) tutorial.) In a future lesson, we will learn how to use this value to retrieve the contents of a file.
The value of the `cid` is a CID object (Content Identifier), a unique address generated from the content of the node. (For a more in-depth look at how CIDs are generated and why they're important, check out our [Decentralized data structures](https://proto.school/#/data-structures) tutorial.) In a future lesson, we will learn how to use this value to retrieve the contents of a file.

The `add` method accepts other `data` formats besides the `File` object and offers many advanced `options` for setting your chunk size and hashing algorithm, pinning files as they're added, and more. We're highlighting the basics in this tutorial, but you can check out the [full documentation](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#add) to learn more.
10 changes: 7 additions & 3 deletions src/tutorials/0005-regular-files-api/04.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,29 @@ const validate = async (result, ipfs) => {
}

const code = `/* global ipfs */
const concat = require('it-concat')

const run = async () => {
const fileContents = // place your code here
const fileContents = await concat() // place your code here

// don't forget to return the string value
}
return run
`

const solution = `/* global ipfs */
const concat = require('it-concat')

const run = async () => {
const fileContents = await ipfs.cat('QmWCscor6qWPdx53zEQmZvQvuWQYxx1ARRCXwYVE4s9wzJ')
const fileContents = await concat(ipfs.cat('QmWCscor6qWPdx53zEQmZvQvuWQYxx1ARRCXwYVE4s9wzJ'))
const message = fileContents.toString()

return message
}
return run
`

const modules = { cids: require('cids') }
const modules = { 'it-concat': require('it-concat') }

const options = {
overrideErrors: true,
Expand Down
19 changes: 11 additions & 8 deletions src/tutorials/0005-regular-files-api/04.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
type: "code"
---

In the previous lesson, you saw that each file added to IPFS has its own unique `hash` derived from its content. This `hash`, also known as a [CID (Content Identifier)](https://proto.school/#/data-structures/04), can be used like an address to access the file. If you know a file's CID, you can use the `cat` method provided by the Regular IPFS Files API -- similar to the one you may have seen previously in Unix-style systems -- to retrieve its contents like so:
In the previous lesson, you saw that each file added to IPFS has its own unique `cid` derived from its content. This [CID (Content Identifier)](https://proto.school/#/data-structures/04), can be used like an address to access the file. If you know a file's CID, you can use the `cat` method provided by the Regular IPFS Files API -- similar to the one you may have seen previously in Unix-style systems -- to retrieve its contents like so:

```javascript
await ipfs.cat(ipfsPath, [options])
ipfs.cat(ipfsPath, [options])
```

An IPFS `path` can take several forms (you can read about them in the [Files API](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#cat) documentation). In this lesson, we'll use the simplest one: the `hash` string (CID) present in the object we receive for a file when the `ipfs.add` method is called. You may remember from the previous lesson that the `add` method returned both `path` and `hash` values for each file you added to IPFS. We'll learn more about the difference between these two values later.
An IPFS `path` can take several forms (you can read about them in the [Files API](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#cat) documentation). In this lesson, we'll use the simplest one: the `cid` present in the object we receive for a file when the `ipfs.add` method is called. You may remember from the previous lesson that the `add` method returned both `path` and `cid` values for each file you added to IPFS. We'll learn more about the difference between these two values later.

The `cat` method first searches your own node for the file requested, and if it can't find it there, it will attempt to find it on the broader IPFS network. Because of the way cryptographic hashing works, the content you're searching for is guaranteed to match the file found, regardless of which peer is hosting it.

Expand All @@ -18,13 +18,16 @@ Data returned by the `cat` method comes in the form of a `Buffer`. A `Buffer` is
So if you had the CID for a text file in an IPFS node, you could retrieve the file's contents as a readable string like so:

```javascript
let bufferedContents = await ipfs.cat('QmWCscor6qWPdx53zEQmZvQvuWQYxx1ARRCXwYVE4s9wzJ') // returns a Buffer
let stringContents = bufferedContents.toString() // returns a string
const concat = require('it-concat')

const bufferedContents = await concat(ipfs.cat('QmWCscor6qWPdx53zEQmZvQvuWQYxx1ARRCXwYVE4s9wzJ')) // returns a Buffer
const stringContents = bufferedContents.toString() // returns a string
```
Notice how the example above waits for the results of the `cat` method before converting the value to a string. To accomplish that in a single line of code, you'd need to wrap the `await` statement in parentheses like so:

Notice how the example above waits for the concatenation of the results of the `cat` method (using the `it-concat` package) before converting the value to a string. To accomplish that in a single line of code, you'd need to wrap the `await` statement in parentheses like so:

```javascript
let stringContents = (await ipfs.cat(cid)).toString() // returns a string
const stringContents = (await concat(ipfs.cat(cid))).toString() // returns a string
```

When you're ready to try this in the real world, you should note that the `cat` method can result in heavy memory usage, depending on the contents of the file being read. If you find this to be the case, you might want to explore the [catReadableStream](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#catreadablestream) or [catPullStream](https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#catpullstream) methods instead.
When you're ready to try this in the real world, you should note that the above example can result in heavy memory usage, depending on the contents of the file being read. If you find this to be the case, you might want to skip the usage of the `concat` package and iterate over each chunk of data iteratively.
Loading