Skip to content

fix: lost references to the files.add method changed to files.addAll #493

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 5 commits into from
Jul 29, 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
2 changes: 1 addition & 1 deletion src/static/tutorials.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
"title": "Regular Files API",
"description": "The IPFS Regular Files API provides a way to store and share files in a peer-to-peer storage system.",
"newMessage": "",
"updateMessage": "Version `0.48` of `js-ipfs` has introduced some changes, most notably by adding `files.addAll` to use with multiple files, while `files.add` can now only be used with a single file. This means that **some code challenge solutions have changed**. We recommend re-visiting this tutorial to learn about the updated methods and complete the revised challenges!",
"updateMessage": "Version `0.48` of `js-ipfs` has introduced some changes, most notably by adding `addAll` to use with multiple files, while `add` can now only be used with a single file. This means that **some code challenge solutions have changed**. We recommend re-visiting this tutorial to learn about the updated methods and complete the revised challenges!",
"createdAt": "2019-01-01T00:00:00.000Z",
"updatedAt": "2020-07-20T00:00:00.000Z",
"resources": [
Expand Down
7 changes: 4 additions & 3 deletions src/tutorials/0005-regular-files-api/03-challenge.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Add the files in your browser (available in the `files` array below) to your IPFS node using the `add` method.
Add the files in your browser (available in the `files` array below) to your IPFS node using the `addAll` method. Alternatively, you can use the `add` method to upload a single file. Return the results.

**Hints:**
- You can pass either a single `File` object or an array of `File` objects to the `add` method.
- You need to concatenate all the results into an array because the `ipfs.add` method returns an `Async Iterable`. You can use either the `for await...of` loop or the function `all`.
- You can either pass an array of `File` objects to the `addAll` method or a single `File` object to the `add` method.
- When using the `addAll` method, you'll need to concatenate all the results into an array because the method returns an `Async Iterable`. You can use either the `for await...of` loop or the function `all` to do this.
- When uploading a single file via the `add` method, be sure to pass only the file to the `add` method, instead of passing the whole `files` array.
84 changes: 57 additions & 27 deletions src/tutorials/0005-regular-files-api/03.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,10 @@ import all from 'it-all'

import utils from '../utils'

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

async function validateMultipleFiles (result, uploadedFiles, { ipfs }) {
const iterable = ipfs.addAll(window.uploadedFiles)
const expectedResult = await all(iterable)

if (!result) {
return {
fail: utils.validationMessages.NO_RESULT
}
}

if (result.error) {
return { error: result.error }
}

if (utils.validators.isAsyncIterable(result)) {
return {
fail: utils.validationMessages.VALUE_IS_ASYNC_ITERABLE_ALL
}
}

if (!Array.isArray(result)) {
return {
fail: 'The returned value should be an array.'
}
}

if (result.length > uploadedFiles.length) {
return {
fail: 'The array you returned has more items than the number of files you uploaded. Be sure to add each file to IPFS just once, which you can do most easily by passing the whole array to the `add` method.'
Expand All @@ -50,10 +26,10 @@ const validate = async (result, ipfs) => {
return {
success: utils.validationMessages.SUCCESS,
logDesc: [
"Your `addAll` command returned the array of objects below. The output is very long because the CID is represented as an `Object` internally, but if you scroll down we'll offer you a more condensed view.",
"You returned the array of objects below. The output is very long because the CID is represented as an `Object` internally, but if you scroll down we'll offer you a more condensed view.",
`<pre class="code-highlight"><code class="hljs json">${JSON.stringify(result, null, 2)}</code></pre>`,
'To simplify the output, we can use the `toString()` method on the `cid` property to get the CID in string format: `QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn`. In future lessons we\'ll always show this simplified version to make it easier to read, as shown below. <br/> <br/>',
'Your `addAll` 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."
'You 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."
].join(' '),
log: result.map(utils.format.ipfsObject)
}
Expand All @@ -62,6 +38,57 @@ const validate = async (result, ipfs) => {
}
}

async function validateSingleFile (result, uploadedFiles, { ipfs }) {
const expectedResult = await ipfs.add(uploadedFiles[0])

if (uploadedFiles.length > 1) {
return {
fail: 'You uploaded multiple files, but you returned only one single file. To upload a single file with `add`, click "Reset Files" to upload a single file to the code editor. To upload multiple files with `addAll`, be sure to concatenate the Async Iterable result from `addAll` into an array.'
}
}

if (JSON.stringify(expectedResult) === JSON.stringify(result)) {
return {
success: utils.validationMessages.SUCCESS,
logDesc: [
"You returned the object bellow. The output is very long because the CID is represented as an `Object` internally, but if you scroll down we'll offer you a more condensed view.",
`<pre class="code-highlight"><code class="hljs json">${JSON.stringify(result, null, 2)}</code></pre>`,
'To simplify the output, we can use the `toString()` method on the `cid` property to get the CID in string format: `QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn`. In future lessons we\'ll always show this simplified version to make it easier to read, as shown below. <br/> <br/>',
"You returned the object below. Notice in particular the `cid` value, since we'll need it to access this file again later. The `path` matches the `cid` for this file, but we'll see in future lessons that that's not always true."
].join(' '),
log: utils.format.ipfsObject(result)
}
} 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 validate = async (result, ipfs) => {
const uploadedFiles = window.uploadedFiles || false

if (!result) {
return {
fail: utils.validationMessages.NO_RESULT
}
}

if (result.error) {
return { error: result.error }
}

if (utils.validators.isAsyncIterable(result)) {
return {
fail: utils.validationMessages.VALUE_IS_ASYNC_ITERABLE_ALL
}
}

if (Array.isArray(result)) {
return validateMultipleFiles(result, uploadedFiles, { ipfs })
} else {
return validateSingleFile(result, uploadedFiles, { ipfs })
}
}

const code = `/* global ipfs, all */

const run = async (files) => {
Expand All @@ -84,6 +111,9 @@ const run = async (files) => {
// result.push(resultPart)
//}

// or when uploading one file
// const result = await ipfs.add(files[0])

return result
}
return run
Expand Down
12 changes: 7 additions & 5 deletions src/tutorials/0005-regular-files-api/03.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ We create these IPFS nodes behind the scenes so that you can focus on the conten

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

## Add a file
## Add a single file with `add`

In order to make a file available on the IPFS network, you first need to add it to a specific IPFS node. It's important to remember that because IPFS is a peer-to-peer, decentralized system, adding a file doesn't mean uploading it to a remote server somewhere. Assuming you're using a node on your own machine, the process is more like picking a file from your computer and adding a label to it that says, "I'm shared on IPFS! My name is ______. Come find me!" That label includes a Content Identifier (CID) derived from the file's contents that serves as a type of address that other peers can use to find a specific file, regardless of whose computer it's hosted on.

Expand Down Expand Up @@ -39,15 +39,17 @@ The value of the variable `result` is an object in the following format:
}
```

## Add multiple files with `addAll`

If you have more than one adorable animal photo to add to the node, use the `addAll` method instead:

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

Because the `addAll` method returns an [`Async Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of), you can only iterate over the values, one by one. If you need to return all the values, you can save each one into an array and then return the array.
Because the `addAll` method returns an [`Async Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of), you can only iterate over its values, one by one. If you need to return all the values, you can save each one into an array and then return the array.

To iterate over all the values, we can use a `for await...of` loop:
To iterate over all the values of the `Async Iterable`, we can use a `for await...of` loop:

```javascript
const result = []
Expand All @@ -68,7 +70,7 @@ To make things easier, we can use the [`it-all`](https://www.npmjs.com/package/i
const result = await all(ipfs.addAll([catPic, dogPic, giraffePic]))
```

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

```javascript
{
Expand All @@ -81,4 +83,4 @@ The value of the variable `result` is an array of objects identical to the outpu

The value of the `cid` is a CID object (Content Identifier), a unique address generated from the contents of each file. (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/js-ipfs/blob/master/docs/core-api/FILES.md#ipfsadddata-options) to learn more.
The `add` and `addAll` methods accept other data formats besides the `File` object and offer 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 for the [`add`](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md#ipfsadddata-options) or [`addAll`](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md#ipfsaddallsource-options) methods to learn more.
4 changes: 2 additions & 2 deletions src/tutorials/0005-regular-files-api/05-challenge.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Add one or more files to your IPFS node, using `{ wrapWithDirectory: true }` to put them in a directory. Because you're targeting the top-level directory, not a subdirectory, the `path` of each file should just be its name.
Add multiple files to your IPFS node with `addAll`, using `{ wrapWithDirectory: true }` to put them in a directory. Because you're targeting the top-level directory, not a subdirectory, the `path` of each file should just be its name.

**Hints:**
- Be sure to reference the examples above for the object structure needed to indicate the desired path of each file, as well as how to pass in multiple files as an array.
- Try the [`map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) or [`forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) array methods to loop through each file in the `files` array and access its name as `file.name`.
- You need to concatenate all the results into an array because the `ipfs.add` method returns an `Async Iterable`. You can use either the `for await...of` loop or the function `all`.
- You need to concatenate all the results into an array because the `ipfs.addAll` method returns an `Async Iterable`. You can use either the `for await...of` loop or the function `all`.
6 changes: 3 additions & 3 deletions src/tutorials/0005-regular-files-api/05.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const validate = async (result, ipfs) => {
const resultingFiles = await pTimeout(all(ipfs.ls(result[result.length - 1].cid.toString())), 5000).catch(() => 'error')
if (resultingFiles === 'error') {
return {
fail: 'Could not get CID of top-level directory. Please make sure you are returning the result of the `add` method. The items of the array should be objects with a `cid` attribute whose value must be a valid CID.'
fail: 'Could not get CID of top-level directory. Please make sure you are returning the result of the `addAll` method. The items of the array should be objects with a `cid` attribute whose value must be a valid CID.'
}
} else {
if (resultingFiles.length !== uploadedFiles.length) {
Expand All @@ -74,9 +74,9 @@ const validate = async (result, ipfs) => {
if (JSON.stringify(result) === JSON.stringify(expectedResults)) {
return {
success: 'Success!',
logDesc: "Here are the results returned by the `add` method. Note that you have one object for each file, plus one for each directory created by the `{ wrapWithDirectory: true }` option (in this case, just the top-level directory with path `''`)." +
logDesc: "Here are the results returned by the `addAll` method. Note that you have one object for each file, plus one for each directory created by the `{ wrapWithDirectory: true }` option (in this case, just the top-level directory with path `''`)." +
"\n\n Because you used the `{ wrapWithDirectory: true }` option, the `path` of each file is now the filename you provided, rather than matching the file's `cid`. You'll be able to use these human-readable paths to in combination with the directory's CID to access the file's content in a future lesson." +
"\n\n We only have access to the added files' and directories' CIDs when the `add` method returns them. When you use this method in the future, you may want to save them for later use.",
"\n\n We only have access to the added files' and directories' CIDs when the `addAll` method returns them. When you use this method in the future, you may want to save them for later use.",
log: result.map(utils.format.ipfsObject)
}
} else {
Expand Down
Loading