Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

feat(breaking change): stats.bwReadableStream and stats.bwPullStream #211

Merged
merged 1 commit into from
Feb 7, 2018

Conversation

hacdias
Copy link
Contributor

@hacdias hacdias commented Jan 30, 2018

More details on ipfs-inactive/js-ipfs-http-client#686

This also closes #212

@hacdias
Copy link
Contributor Author

hacdias commented Jan 31, 2018

Needed for ipfs/js-ipfs#1198 /cc @diasdavid 😄

@victorb
Copy link
Contributor

victorb commented Jan 31, 2018

@hacdias this should be a argument to the function that changes if it just returns the values once or if you get a stream back, rather than just having one of them.

The different signatures would be something like this:

ipfs.stats.bw((err, oneTimeRes) => {})

or

ipfs.stats.bw({poll: true}, () => {err, stream})

@hacdias
Copy link
Contributor Author

hacdias commented Jan 31, 2018

Sure! Makes sense. Will change that @victorbjelkholm

@hacdias hacdias force-pushed the stats-bw branch 3 times, most recently from c9aedbf to 17aebbf Compare January 31, 2018 08:34
@hacdias
Copy link
Contributor Author

hacdias commented Jan 31, 2018

@victorbjelkholm done 😄

SPEC/STATS.md Outdated
`callback` must follow `function (err, stats) {}` signature, where `err` is an error if the operation was not successful. `stats` is an Object containing the following keys:
If `poll` is `true`, then `callback` must follow `function (err, stream) {}` signature, where `err` is an error if the operation was not successful and `stream` is a Readable Stream where you can listen to the event `data` with a listener that must follow `function (stat) {}` signature.

Otherwise, `callback` must follow `function (err, stat) {}` signature, where `err` is an error and `stat` is an Object containing the following keys:
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 define what the values are as well, I think the values are defined in bytes, but unsure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

? The stat thing on the stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If so, it is an object like the other one. That's why I left them with the same name and explained in the end.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that stat object. We say which keys are on the object, but not what the values are.

Copy link
Contributor

Choose a reason for hiding this comment

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

Problem is not understanding that stat is used in both cases, but what the values are. Are they bytes? Are they kilobytes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add that!

js/src/stats.js Outdated
expect(err).to.not.exist()
expect(res).to.exist()

res.once('data', (data) => {
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 have a way of closing the stream as well, for when you're done polling for stats

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@victorbjelkholm probably stream.destroy() is our best shot

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added res.destroy() in the end.

js/src/stats.js Outdated
expect(res).to.exist()

res.once('data', (data) => {
expect(data).to.have.a.property('totalIn')
Copy link
Contributor

Choose a reason for hiding this comment

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

This expectation only checks that the property exists, which I don't think is good enough. These test cases would still pass even if totalIn is set to for example false, undefined or even null, which is not correct.

Copy link
Contributor

Choose a reason for hiding this comment

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

Noticed that the two test cases are the same as well. One should test with {poll: true} and the other one without passing any extra args.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have 4 test cases: 2 for callbacks and two for promises. One of those two has {poll: true} and the other has no args.

Right now, those values are strings. Should I convert them to something like big.js?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a try catch to check if they are numbers using big.js.

js/src/stats.js Outdated
done()
})
}).catch(err => {
expect(err).to.not.exist()
Copy link
Contributor

Choose a reason for hiding this comment

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

can just do .catch(done) instead as done with a error set will fail the test case

Copy link
Contributor

Choose a reason for hiding this comment

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

Since you are using Promises, you can remove the done and just make sure that you return the promise. The way it is now you don't get any benefit from using the Promise syntax over Callbacks

js/src/stats.js Outdated
done()
})
}).catch(err => {
expect(err).to.not.exist()
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you are using Promises, you can remove the done and just make sure that you return the promise. The way it is now you don't get any benefit from using the Promise syntax over Callbacks

SPEC/STATS.md Outdated
@@ -25,7 +25,11 @@ Where:
- `poll` is used to print bandwidth at an interval.
- `interval` is the time interval to wait between updating output, if `poll` is true.

`callback` must follow `function (err, stats) {}` signature, where `err` is an error if the operation was not successful. `stats` is an Object containing the following keys:
If `poll` is `true`, then `callback` must follow `function (err, stream) {}` signature, where `err` is an error if the operation was not successful and `stream` is a Readable Stream where you can listen to the event `data` with a listener that must follow `function (stat) {}` signature.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why ReadableStream and not pull-stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can use a pull-stream, but I didn't find a way to do this with that package...

Copy link
Contributor Author

@hacdias hacdias Jan 31, 2018

Choose a reason for hiding this comment

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

@diasdavid Also, js-ipfs-api doesn't seem to be using pull streams. at least the package isn't a dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And how can we close a pull-stream? Their docs say nothing about it. The Readable Stream seems to be more straightforward and easy to use. You just have to do .on('data', fn).

@hacdias
Copy link
Contributor Author

hacdias commented Jan 31, 2018

@victorbjelkholm I made some updates so this also fixes #212

@hacdias
Copy link
Contributor Author

hacdias commented Jan 31, 2018

@diasdavid, @victorbjelkholm and I discussed about using Readable Stream or Pull Stream and he pointed me to ipfs/js-ipfs#557 (comment). Then we noticed that it doesn't make much sense to have three different functions for this API since it is really simple.

Right now, I have made the implementation to use Readable Stream here. Do you think we should use pull-stream? If so, why and could you point me out some places to look to see how I should implement this because their docs didn't help me much.

@daviddias
Copy link
Contributor

@hacdias there are some more bits that are important for you to get familiar with. Glad that you found ipfs/js-ipfs#557, make sure to also:

Then we noticed that it doesn't make much sense to have three different functions for this API since it is really simple.

I feel that the argument that you are trying to make is that "this is not a very used function and so a simple thing will though", which can be fair, however, it is important to understand the future implications and make decisions that will enable users to migrate without having to follow a changing interface. Two cery good threads for you to read: https://unix.stackexchange.com/questions/235335/the-linux-kernel-breaking-user-space & https://felipec.wordpress.com/2013/10/07/the-linux-way/

Let's follow up once you went through all of these materials :)

js/src/repo.js Outdated
expect(isNumber(res.repoSize)).to.eql(true)
expect(isNumber(res.storageMax)).to.eql(true)
expect(res.repoPath).to.be.a('string')
expect(res.version).to.be.a('string')
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still not valid as expect('').to.be.a('string') would pass the tests.

js/src/stats.js Outdated

expect(isNumber(res.provideBufLen)).to.eql(true)
expect(res.wantlist).to.be.an('array')
expect(res.peers).to.be.an('array')
Copy link
Contributor

Choose a reason for hiding this comment

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

Would we consider this test to be correct if res.peers was a empty array?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes and it might be empty in some cases.

js/src/repo.js Outdated
chai.use(dirtyChai)

const isNumber = (n) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually checking if n is a BigInt, not Number, which is a built-in type in JavaScript

js/src/repo.js Outdated
expect(stat).to.have.property('repoPath')
expect(stat).to.have.property('version')
expect(stat).to.have.property('storageMax')
expect(res).to.exist()
Copy link
Contributor

@victorb victorb Feb 1, 2018

Choose a reason for hiding this comment

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

These assertions are repeated all over the tests. We should extract the assertions into a function and pass in res instead.

const expectIsStat = (stat) => {
  // Make all the assertions once here
}

SPEC/STATS.md Outdated

`stat` is, in both cases, an Object containing the following keys:

- `totalIn` - is a string containing a uint64 in bytes.
Copy link
Contributor

Choose a reason for hiding this comment

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

These are now Big values rather than strings.

SPEC/STATS.md Outdated
@@ -25,12 +25,16 @@ Where:
- `poll` is used to print bandwidth at an interval.
- `interval` is the time interval to wait between updating output, if `poll` is true.

`callback` must follow `function (err, stats) {}` signature, where `err` is an error if the operation was not successful. `stats` is an Object containing the following keys:
If `poll` is `true`, then `callback` must follow `function (err, stream) {}` signature, where `err` is an error if the operation was not successful and `stream` is a Pull Stream where you can listen to the event `data` with a listener that must follow `function (stat) {}` signature.
Copy link
Contributor

Choose a reason for hiding this comment

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

@hacdias this wasn't necessarily what I was hoping for here. Follow the example of https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/FILES.md#files-api and create two api calls.

  • ipfs.stats.bw - single requests
  • ipfs.stats.bwPoll - return a stream

Copy link
Contributor Author

Choose a reason for hiding this comment

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

bwPoll returns which kind of stream?

Copy link
Contributor

Choose a reason for hiding this comment

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

  • ipfs.stats.bwPollPullStream
  • ipfs.stats.bwPollReadableStream

🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd just suggest to take the 'Poll' out of the equation and use this three functions:

  • stats.bw
  • stats.bwPullStream
  • stats.bwReadableStream

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think @diasdavid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

I suggest to structure the SPEC like that instead of creating different entries for each function and add examples for each of them. In FILES spec, we have:

image

Which is not accurate: we have one API endpoint which is cat. The other functions should be called variants or something like that and not separated in the SPEC. Why? Duplicated information (on the parameters for example) and then we have that GO - WIP thing which may make users think there will be catPullStream or [insert your command here]ReadableStream on Go. That's something I'd like to work on after finishing this Pull Request and make this interface documentation a little better. What do you think @diasdavid?

Copy link
Contributor

Choose a reason for hiding this comment

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

@hacdias let's avoid painting the 🚲🏡 in this PR. tl;dr; they are top level functions, they should be listed in the table of contents and have urls to it.

@hacdias hacdias force-pushed the stats-bw branch 2 times, most recently from 4432dc9 to c76eab4 Compare February 6, 2018 14:29
@hacdias
Copy link
Contributor Author

hacdias commented Feb 6, 2018

@diasdavid @victorbjelkholm could you please take a look at the SPEC I wrote? If you approve it, I can start implementing it on js-ipfs-api and create the tests here 😄

@daviddias
Copy link
Contributor

@hacdias go for it :)

@hacdias hacdias changed the title feat(breaking change): use stream on stats.bw feat(breaking change): stats.bwReadableStream and stats.bwPullStream Feb 7, 2018
@hacdias
Copy link
Contributor Author

hacdias commented Feb 7, 2018

I think this is ready @diasdavid :)

@daviddias daviddias merged commit 4421eb2 into master Feb 7, 2018
@daviddias daviddias deleted the stats-bw branch February 7, 2018 09:42
@ghost ghost removed the in progress label Feb 7, 2018
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.

Many tests without actual assertions
3 participants