Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Add additional publish function for more fine grained control of IPNS record publishing #91

Merged
merged 7 commits into from
Jul 6, 2018

Conversation

bonedaddy
Copy link
Contributor

Hello,

I recently ran into an issue with go-ipfs-api where I wanted additional control over IPNS record publishing (key specification, ttl, etc..., retrieving the response) so I have created an additional function PublishWithDetails, which returns the response from the IPFS daemon as well as allows for manipulation of the api parameters.

@Stebalien Stebalien requested review from a user and magik6k June 26, 2018 03:37
Copy link
Member

@magik6k magik6k left a comment

Choose a reason for hiding this comment

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

Thanks!

I'm not exactly in favour of adding new stuff here as it will eventually get replaced with coreapi implementation, but this seems useful enough.

Few things:

  • Some stuff in code
  • A test or two would be great
    • currently we don't test ipns here, and it might be a bit flaky, so feel free to skip this part.
  • Instead of erroring on missing args, I'd make the arguments optional

ipns.go Outdated
resolveString = "false"
}
args := []string{contentHash, resolveString, lifetime, ttl, key}
resp, err := s.newRequest(context.TODO(), "name/publish", args...).Send(s.httpcli)
Copy link
Member

Choose a reason for hiding this comment

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

should be context.Background() as in other places

ipns.go Outdated
if contentHash == "" {
return nil, errors.New("empty contentHash provided")
}
if lifetime == "" {
Copy link
Member

Choose a reason for hiding this comment

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

I'd pass this as time.Duration

ipns.go Outdated
if lifetime == "" {
return nil, errors.New("empty lifetime provided")
}
if ttl == "" {
Copy link
Member

Choose a reason for hiding this comment

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

Same with time.Duration

ipns.go Outdated
if key == "" {
return nil, errors.New("empty key provided")
}
if resolve {
Copy link
Member

Choose a reason for hiding this comment

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

can use strconv.FormatBool

@bonedaddy
Copy link
Contributor Author

Thanks for the review, all good suggestions. I'll implement the changes you suggested and try to spec out some tests for this function (I don't think this part should be toooo difficult since it's just one function call). Depending on how busy my work week is I should have this done ~Friday. Thanks again.

At the expense of slight duplicated code, I create two new functions getURLIPNS and SendIPNS to accommodate for how the daemon handles web requests. Currently when using `Send` every argument gets formatted with `arg=....&arg=....` causing defaults to be used for many arguments. This was preventing publishing of records using non-self keys as the arguments for the API calls have to be named.
@bonedaddy
Copy link
Contributor Author

bonedaddy commented Jun 27, 2018

@magik6k So I had to create some slight duplicated code. While writing the tests, I discovered that no matter what, I was unable to sign IPNS entries with keys other than self. I played around with the code and it turned that that when calling Send on the the Request type, the getURL function formats all the provided arguments like so arg=.....&arg=....

so when making the API call you had a URL that looked like

http://localhost:5001/api/v0/name/publish?arg=QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V&arg=true&arg=1h&arg=1h&argkey

instead of

http://localhost:5001/api/v0/name/publish?arg=QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V&resolve=true&lifetime=1h&ttl=1h&key=key

This would cause system defaults to be used for the key, lifetime, ttl, etc....

I was able to get around this without impacting any existing functionality by creating special functions to be called when publishing IPNS entries. After making the changes I was able to publish IPNS entries using any keys.

@magik6k
Copy link
Member

magik6k commented Jun 27, 2018

Did you try to use req.Opts, like in there:

go-ipfs-api/shell.go

Lines 172 to 186 in de157ca

req := NewRequest(context.Background(), s.url, "add")
req.Body = fileReader
req.Opts["progress"] = "false"
if !pin {
req.Opts["pin"] = "false"
}
if rawLeaves {
req.Opts["raw-leaves"] = "true"
}
resp, err := req.Send(s.httpcli)
if err != nil {
return "", err
}

@bonedaddy
Copy link
Contributor Author

bonedaddy commented Jun 27, 2018

Oh I'm dumb haha I didn't see that! I'll try with that, thanks.

Update: Works perfectly! Thanks for the suggestion much cleaner now.

Copy link
Member

@magik6k magik6k left a comment

Choose a reason for hiding this comment

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

Thanks for the effort!

I'd just skip the tests (with t.Skip() at the begging of each test), as I can't think of an easy way of executing them without interfering with the daemon.

Other than that, one nitpick and it should be good to go!

ipns_test.go Outdated
var examplesHash = "/ipfs/Qmbu7x6gJbsKDcseQv66pSbUcAA3Au6f7MfTYVXwvBxN2K"

func TestPublishDetailsWithKey(t *testing.T) {
shell := ipfsapi.NewShell("localhost:5001")
Copy link
Member

Choose a reason for hiding this comment

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

other tests do NewShell(shellUrl)

Copy link
Member

Choose a reason for hiding this comment

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

(Note that you'll need to change the package to just shell)

ipns_test.go Outdated
func TestPublishDetailsWithKey(t *testing.T) {
shell := ipfsapi.NewShell("localhost:5001")

resp, err := shell.PublishWithDetails(examplesHash, "key1", time.Hour, time.Hour, false)
Copy link
Member

Choose a reason for hiding this comment

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

This expects key1 to be generated when calling this

ipns_test.go Outdated
func TestPublishDetailsWithoutKey(t *testing.T) {
shell := ipfsapi.NewShell("localhost:5001")

resp, err := shell.PublishWithDetails(examplesHash, "", time.Hour, time.Hour, false)
Copy link
Member

Choose a reason for hiding this comment

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

This will publish over whatever is published on the machine it's invoked on, which is.. suboptimal

ipns.go Outdated
key = "self"
}
req.Opts["key"] = key
req.Opts["lifetime"] = lifetime.String()
Copy link
Member

Choose a reason for hiding this comment

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

I'd wrap this and ttl with an if and only set this when duration is > 0

go-ipfs cares whether or not a option is present - https://github.com/ipfs/go-ipfs/blob/master/core/commands/publish.go#L117

@bonedaddy
Copy link
Contributor Author

bonedaddy commented Jun 27, 2018

@magik6k No problem! Happy to be of some help, you all have been doing fantastic work with IPFS and the related protocols, kudos. I added t.Skip() to the beginning of the tests so we don't interfere with any daemons.

I haven't done complete tests on this yet and its not really related to this PR so feel free to ignore, but it appears that if my node tries to publish an IPNS entry for content that it does not have locally via cache or pinning then the node hangs on the publishing process indefinitely. I tried this last week and I let my node run for 15 minutes before killing the process.

Copy link
Member

@magik6k magik6k left a comment

Choose a reason for hiding this comment

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

LGTM (with 1 tiny nitpick)

ipns.go Outdated
@@ -25,6 +33,36 @@ func (s *Shell) Publish(node string, value string) error {
return nil
}

// PublishWithDetails is used for fine grained control over record publishing
func (s *Shell) PublishWithDetails(contentHash, key string, lifetime, ttl time.Duration, resolve bool) (*PublishResponse, error) {
var pubResp PublishResponse
Copy link
Member

Choose a reason for hiding this comment

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

I'd put it above/closer to json.Unmarshal line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! :D

@magik6k
Copy link
Member

magik6k commented Jul 4, 2018

(needs gofmt, see Travis)

@bonedaddy
Copy link
Contributor Author

Done! Thank you for putting up with some of my silly mistakes! If I submit another PR in the future, I will make sure to thoroughly parse it before-hand to make sure the stuff that came up in this PR, doesn't come up in the future.

@magik6k
Copy link
Member

magik6k commented Jul 6, 2018

Looks good, going to merge now.

Also a note - Commit messages should be scoped by roughly what subsystem they affect, like ipns: ran gofmt instead of just ran gofmt. If you can't think of a good commit name you can usually just merge it with a previous commit (git commit --amend [--no-edit]). Force pushing to PRs is totally fine.

@magik6k magik6k merged commit 9984da6 into ipfs:master Jul 6, 2018
@bonedaddy
Copy link
Contributor Author

Thanks once again! The advice is greatly appreciated 😀

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.

2 participants