Passing params to delete API method using golang


(Richard Phillips) #1

Using Go, I am trying to remove a user from a group using the api. I am confident the endpoint is correct - /groups/148/members.json where 148 is the ID of the group.

From the API spec - it seems I use the DELETE method and pass the user id in the body like this.

{"user_id":36}

When I examine an actual api call from the standard discourse client, it appears to be plain form data like this:

user_id=36

To try achieve this in Golang, I have created form values like this:

form := url.Values{}
form.Add("user_id", strconv.Itoa(discouserid))
body=strings.NewReader(form.Encode())
req, err := http.NewRequest("DELETE", url, body)

I also debugged and checked the sent body contains user_id=36. I also tried this as a json string as shown at the top and that did not work either.

All of these resulted only in a 500 error from the API - I am using Get and Post methods successfully elsewhere which uses the same url / authentication - it is just this delete method which needs form data that I am having trouble with.

Any thoughts>


#2

anything in your error logs - if I remember rightly from my testing before the error 500 writes to the error logs so you can see if anything is missing


(Richard Phillips) #3

Sometimes I need the obvious pointing out to me! Huge thanks. Next thanks go to the Discourse team - I had not previously found the /logs page in the web interface - amazingly valuable and easier than digging around on the server.

Still not fixed, but at least I am not running blind any more!

The logs suggest I am not correctly submitting the formdata correctly… This is response with either the json or plain formatted version:

Discourse::InvalidParameters (user_id or username must be present)
/var/www/discourse/app/controllers/groups_controller.rb:229:in `remove_member'

/var/www/discourse/app/controllers/groups_controller.rb:229:in `remove_member'
/var/www/discourse/vendor/bundle/ruby/2.3.0/gems/actionpack-4.2.9/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
/var/www/discourse/vendor/bundle/ruby/2.3.0/gems/actionpack-4.2.9/lib/abstract_controller/base.rb:198:in `process_action'
/var/www/discourse/vendor/bundle/ruby/2.3.0/gems/actionpack-4.2.9/lib/action_controller/metal/rendering.rb:10:in `process_action'

I am not Ruby literate - but one weird thing is that the line 229 in that source file is not in the ‘remove_member’ function - it is in ‘add_members’ function.

As I understand it, the data user_id for this method should be added as form data - that is what the actual discourse app seems to do, but so far I cannot replicate correctly in Go.


#4

It should be user_id from what I can see in the docs

http://docs.discourse.org/#tag/Groups%2Fpaths%2F~1groups~1{group_id}~1members.json%2Fdelete

What are you getting in the env tab of the logs - it should show you the form data coming through

Stupid question but am guessing you are also sending the api_key and api_username through the form data as well?


(Richard Phillips) #5

Actually I have cracked it. Yes, API auth working fine on all other messages.

After staring at it for a while, I decided to encode the required variable with the URL rather than passing as formdata. This seems to be different to how the Discourse app is working, but it works.

Also, the API documentation only mentions using the user_id - and I noticed it also allows user_email or username. As it happens, username is more convenient for my purpose.

In case anyone passes this way and finds it useful, this is how I adjusted the DELETE method of this library: GitHub - FrenchBen/godisco: A Golang library for the Discourse Forum API - so it is now easy to pass variables to be encoded in either the formdata or query string. In passing the username variable as a url.Value to the querydata - this now works perfectly. Am still not sure why I cannot duplicate the behaviour of the discourse app (not sure I am encoding the formdata correctly) - but it works a charm now and using username is better for me so I am out ahead!

Many thanks again for your help…

// DELETE Method - optionally can take querydata and formdata
func (c *DiscourseClient) Delete(endpoint string, querydata url.Values, formdata url.Values) (error) {

	// Put authentication into the url
	apiAuth := url.Values{}
	apiAuth.Set("api_key", c.key)
	apiAuth.Add("api_username", c.user)

	// This adds the optional querydata settings to the URL
	for key := range querydata {
		apiAuth.Add(key, querydata.Get(key))
	}

	url := fmt.Sprintf("%s%s?%s", c.domain, endpoint, apiAuth.Encode())

	// If supplied, encode the formdata to include in the body
	body := strings.NewReader(formdata.Encode())

	req, err := http.NewRequest("DELETE", url, body)
	if err != nil {
		log.Warn("discoclient.Delete failed", err)
		return err
	}

	// We just need to know whether the delete was a success
	_, err = c.do(req)

	return err
 }