Swagger How to describe JSON body parameter - json

I am trying to add documentation to my Rest API (Gin framework) and I stepped in some problems while trying to structure a JSON body parameter.
Currently, I have the following API description operations:
// #Summary logins a user
// #ID login-user
// #Accept json
// #Produce json
// #Param email formData string true "user email"
// #Param password formData string true "user password"
// #Success 200 {object} gin.H "login response"
// #Failure 400 {object} gin.H "error response"
// #Router /login [post]
func (server *Server) handleLoginUser() gin.HandlerFunc {
return func(ctx *gin.Context) {
var req loginUserRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, utils.ErrorResponse(err))
return
}
// some code
ctx.JSON(http.StatusOK, response)
}
}
When I submit the data through Swagger UI I get the following error:
{
"error": "invalid character 'e' looking for beginning of value"
}
Also, this is the generated cURL:
curl -X 'POST' \
'http://localhost:8080/api/login' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d 'email=my%40email.com&password=password'
It's worth to mention that whenever I submit the same data in Postman with body Raw JSON it works. This is what a usual JSON looks like (also, loginUserRequest):
{
"email": "my#mail.com",
"password": "password"
}
Since I am new to Swagger, I am pretty sure it's something related to the email & password param type defined on the Swagger's [attribute documentation].
So, how should I describe better the loginRequest JSON body?

It was pretty easy, but I guess they omitted that in the documentation. I just changed the parameter as following:
// #Param loginUserRequest body loginUserRequest true "user password"
And then, when running swag init --parseDependency --parseInternal --parseDepth 1 it works.

Related

extra backward slash along with quotes on jsonencode a json array object

In terraform,I am trying to make a PUT call via curl command using null_resource and executing the command in local_exec provisioner. The body of the request is json array.
The command expects the data to be in string format. I have the data as tuple in locals. I am using jsonencode to serialize the tupple to a string and pass to curl. But on jsonencode, the string formed has an additional \ prefixed before each " in json string.
eg. expected json string is:
"[{"key-1" : "value-1", "key-2": "value-2"}]"
But after jsonencode, the formatted string is as follows:
"[{\"key-1\" : \"value-1\", \"key-2\": \"value-2\"}]"
additional backslash is added along with quotes.
Because of the malformed request body, the API returns Bad Request response.
How can we correctly serialize the tupple/list to correct json string in Terraform and pass to curl command?
This is my null_resource:
resource "null_resource" "call_API" {
for_each = { for x in local.array: x.name => {
name = x.name
vars = jsonencode(x.vars)
} }
provisioner "local-exec" {
command = <<EOF
curl -X PUT ${var.url} -d ${each.value.vars} -H 'Content-Type:application/json' -H 'Authorization:${local.bearer_token}'
EOF
}
I think I understand that you want to pass this variable
"[{"key-1" : "value-1", "key-2": "value-2"}]"
You tried to use EOF?
You can do something like that
variable "body" {
type = "string"
}
variable = <<EOF
"[{"key-1" : "value-1", "key-2": "value-2"}]"
EOF
Or with EOT, honestly I don't remember which one work
EDIT:
If the json is not static and you receive it from other module or others, you can try to expand the variable inside the EOFs
body = <<EOF
${var.json}
EOF

exec.Command Escaping Variables with JSON Payload

Thank you in advance as I have spent 2 days on this. Here is a working curl command.
curl -ku login:pass -X POST -H 'Content-Type: application/json'-d'{"type":"page","title":"Testpage","space":{"key":"ITDept"},"body":{"storage":{"value":"<p>Blank Page.</p>","representation":"storage"}}}' https://confluence/rest/api/content
I need to get this to execute with exec.Command.
Given that now in Go I have tried escaping and all sorts of other means to get this to work. The issue is more than likely this ridiculous JSON string that is required. I have the JSON string saved into a var now to try it that way.
jsonPayload := '{"type":"page","title":"Testpage","space":{"key":"ITDept"},"body":{"storage":{"value":"<p>Blank Page.</p>","representation":"storage"}}}'
execCmd := "bash", "-c", "curl -ku login:pass -X POST -H 'Content-Type: application/json' -d" jsonPayload "https://confluence/rest/api/content"
So the jsonPayload is the argument to -d. I have tried this using the Marshal json/encoding and the net/http package and it goes through but something about how that stdlib is sending it causes the API to state it is the wrong format.
I also have tried this with this and the curl copied out of the println works but when actually ran in golang it fails with incorrect format.
env := os.Environ()
curlCmd, err := exec.LookPath("curl")
if err != nil {
fmt.Println("Path not found to binary!")
panic(err)
}
args := []string{"curl", "-ku", "login:pass", "-X", "POST", "-H", "'Content-Type: application/json'", "-d", payloadJson, "https://confluence/rest/api/content"}
execErr := syscall.Exec(curlcmd, args, env)
if execErr != nil {
panic(execErr)
}
fmt.Println(curlCmd)
When the curlCmd from that last line there prints it can be copied and pasted into the terminal and it works however when going through golang it comes with a format not supported. Any help would be greatly appreciated.
Try this:
payload := `{"type":"page","title":"Testpage","space":{"key":"ITDept"},"body":{"storage":{"value":"<p>Blank Page.</p>","representation":"storage"}}}`
cmd := exec.Command("curl", "-ku", "login:pass", "-X", "POST", "-H", "Content-Type: application/json", "-d", payload, "http://localhost:8080/confluence/rest/api/content")
p, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", p)
Important change from code in the question:
Run command directly instead of using bash.
Specify host name in URL.
Properly quote the string.
BTW, you can also an interpreted string literal:
payload := "{\"type\":\"page\",\"title\":\"Testpage\",\"space\":{\"key\":\"ITDept\"},\"body\":{\"storage\":{\"value\":\"<p>Blank Page.</p>\",\"representation\":\"storage\"}}}"

How to add a UTF-8 String of JSON within JSON in Curl?

I'm creating a menu for my Facebook bot by posting some JSON to their API. The payload is required to be a UTF-8 string. I would like this string to be JSON such that my server can read it easily. The following results in the error "setting_type" is required which indicates something is not being escaped properly:
curl -X POST -H "Content-Type: application/json" -d '{
"setting_type" : "call_to_actions",
"thread_state" : "existing_thread",
"call_to_actions":[
{
"type":"postback",
"title":"Schedule",
"payload":"{"action":"schedule"}"
},
{
"type":"postback",
"title":"Cancel",
"payload":"{"action":"cancel"}"
},
{
"type":"postback",
"title":"View",
"payload":"{"action":"view"}"
}
]
}' "https://graph.facebook.com/v2.6/me/thread_settings?access_token=TOKEN"
A solution is to escape the double quotes inside the payload:
curl -X POST -H "Content-Type: application/json" -d '{
"setting_type" : "call_to_actions",
"thread_state" : "existing_thread",
"call_to_actions":[
{
"type":"postback",
"title":"Schedule",
"payload":"{\"action\":\"schedule\"}"
},
{
"type":"postback",
"title":"Cancel",
"payload":"{\"action\":\"cancel\"}"
},
{
"type":"postback",
"title":"View",
"payload":"{\"action\":\"view\"}"
}
]
}' "https://graph.facebook.com/v2.6/me/thread_settings?access_token=TOKEN"
This succeeds. However when the payload is sent to me it looks like this:
"payload":"{\"action\":\"schedule\"}"}
and thus does not parse properly.
UPDATE
I've written the same procedure in Scala using the Play! framework:
def getMenuJson(greeting: String, schedule: String, cancel: String, view: String)(implicit sd: String): JsValue = {
log.info(Json.toJson(BotPayload("schedule", None)).toString())
val menu = Outgoing(
Recipient(sd),
OutMessage(
Some(OutAttachment(
"template",
Payload("button", greeting, List(
Button("postback", None, Some(schedule), Some(Json.toJson(BotPayload("schedule", None)).toString())),
Button("postback", None, Some(cancel), Some(Json.toJson(BotPayload("cancel", None)).toString())),
Button("postback", None, Some(view), Some(Json.toJson(BotPayload("view", None)).toString()))
))
)), None)
)
Json.toJson(menu)
}
The log statement that appears at the top of the function outputs:
{"action":"schedule"}
which implies the JSON in payload is correctly being formatted as a string. Now when a user presses the schedule button from Facebook I get back for the payload:
"payload":"{\"action\":\"schedule\"}"
So perhaps the second Curl command was fine and I just need to deal with parsing the JSON with escaped quotes.
UPDATE 2
Working on parsing the result. This isn't quite working:
implicit val botPayloadReads: Reads[BotPayload] = (
(JsPath \ """\"action\"""").read[String] and
(JsPath \ """"\"returnToAction\"""").readNullable[String]
)(BotPayload.apply _)
Error:
List((/entry(0)/messaging(0)/postback/payload/\"action\",List(ValidationError(List(error.path.missing),WrappedArray()))))

Simple JSON request with cURL to Mochiweb

I have a very simple 'hello world' Mochiweb server (I just started my introduction into it), which takes a JSON request and sends it back:
'POST' ->
case Path of
"dummy" ->
Data = Req:parse_post(),
Json = proplists:get_value("json", Data),
Struct = mochijson2:decode(Json),
Action_value = struct:get_value(<<"action">>, Struct),
Action = list_to_atom(binary_to_list(A)),
Result = [got_json_request, Action],
DataOut = mochijson2:encode(Result),
Req:ok({"application/json",[],[Result]});
The thing is that when I try to make a request to it with cURL it fails:
curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"action":"dummy"}' http://localhost:8080/dummy
The Mochiweb log is quite difficult to read, but as I understand the error happens at this line:
Json = proplists:get_value("json", Data)
I have put a couple of io:formats in the code and found out that both Data and Json variables are [] after I make a request with cURL.
On the other hand, when I do a very simple request with cURL:
curl -d '{"action":"dummy"}' http://localhost:8080/dummy
both Data and Json are [{"{\"action\":\"dummy\"}",[]}], but in that case the line Struct = mochijson2:decode(Json) fails.
For some strange reason Mochiweb does not see the JSON data in the POST request in case the header has the "application/json" value.
So, the question is: How do I make a correct POST request with JSON data to a Mochiweb server?
EDIT: Json variable has the undefined value.
Try something along the lines of
Data = Req:recv_body(),
Json = mochijson2:decode(Data),
...
You should at least ensure method post and the content type ahead of this.
This is not about POST nor get.
It's about how you post your data to send to your server
When you send a json data to server, you need to make it as key=value
curl -d "key=value" "http://your.domain.com/path"
Therefore, if you want to post json as '{"action":"dummy"}', for GET request
curl -d "json='{\"action\":\"dummy\"}'" http://localhost:8080/dummy
For POST request as a file,
curl -F "json=#filename.json" http://localhost:8080/dummy
of course, when you send as a file, you need to read the posted file from the server side.

Remote Transmission session doesn't respond after providing corrent session id

Well, that would be a rather obscure topic but I'll give it a try, maybe someone will know the answer.
I am writing a little remote Node.js client for the Transmission BitTorrent Client. Communication is handled via RPC using JSON objects.
Here is the specification.
And my code (written in CoffeeScript, if that is a problem I can provide the equivalent JavaScript code also, just didn't want to make this question too long to read), the important part:
runRemoteCommand: (params) ->
# convert JSON object to string
params = JSON.stringify params #, null, " "
# set request options
options =
host: #config.host
port: #config.port
path: #config.rpcPath
auth: "#{#config.username}:#{#config.password}"
headers:
'Content-Type': 'application/json'
'Content-Length': params.length
method: 'GET'
# we don't know the session id yet
sessionId = false
# wrapped in a function so it could be run two times, not really a finished solution
run = () =>
# If the session id is provided, set the header, as in 2.3.1 of the specs
if sessionId
options.headers["X-Transmission-Session-Id"] = sessionId
# define the request object and a callback for the response
request = #http.get options, (response) =>
# log everything for debug purposes
console.log "STATUS: #{response.statusCode}"
console.log "HEADERS: #{JSON.stringify response.headers}"
response.setEncoding 'utf8'
response.on "data", (data) =>
console.log "BODY: #{data}"
# if status code is 409, use provided session id
if response.statusCode == 409
sessionId = response.headers["x-transmission-session-id"]
console.log "sessionId: #{sessionId}"
# running it immediately sometimes caused the remote server to provide a 501 error, so I gave it a timeout
setTimeout run, 5000
# no output here
request.on "error", (e) =>
console.log "ERROR: #{e}"
# actually send the request
request.write params
request.end()
# run our function
run()
The params variable is defined as:
params =
"arguments":
"filename": link
"method": "torrent-add"
"tag": 6667
Everything works fine until I set a valid session id. On the first time the run function is called, I get the following output (formatted it a little to be more eye-friendly):
STATUS: 409
HEADERS:
{
"server":"Transmission",
"x-transmission-session-id":"io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR",
"date":"Wed, 04 Apr 2012 08:37:37 GMT",
"content-length":"580",
"content-type":"text/html; charset=ISO-8859-1"
}
sessionId:
io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
BODY: 409:
ConflictYour request had an invalid session-id
header.To fix this, follow these steps: When reading a
response, get its X-Transmission-Session-Id header and remember it
Add the updated header to your outgoing requests When you get this
409 error message, resend your request with the updated
headerThis requirement has been added to help prevent CSRF
attacks.X-Transmission-Session-Id:
io4dOLm8Q33aSCEULW0iv74SeewJ3w1tP21L7qkdS4QktIkR
Which is exactly what should be returned by the remote server when no session id is provided. However, after setting the session id in the header, the server doesn't respond. The second run call is fired and the request is sent (confirmed by placing some useful console.logs), but the response callback is never fired. I receive no response from the remote server and my application freezes waiting.
I'm pretty sure the error is on my side, not on the server's, because an out-of-the-box remote client for android works just fine when connecting to the same remote session.
Am I performing the request correctly? Especially the JSON part?
EDIT: A little test
I have written a little php script to test if the JSON-encoded request is ok and used it as a "fake" remote transmission. Here it is:
$headers = apache_request_headers();
// Simulate transmission's behavior
if (!isset($headers['X-Transmission-Session-Id'])) {
header("HTTP/1.0 409 Conflict");
header("X-Transmission-Session-Id: test");
}
print_r($headers);
// Is there a nicer way to get the raw request?
print_r(file_get_contents('php://input'));
And, personally, I don't see anything wrong in the data outputted by this test. After returning the 409 status code, the Node.js app properly assigns the session id for the request. The first print_r prints an array:
Array
(
[Content-type] => application/json
[Content-length] => 152
[X-Transmission-Session-Id] => test
[Host] => tp.localhost
[Connection] => keep-alive
)
The second one prints a string, which is a properly formatted JSON string (nothing more in it):
{
"arguments": {
"filename": "http://link-to-torrent"
},
"method": "torrent-add",
"tag": 6667
}
I really can't see what am I doing wrong. Some third-party clients which I tested with the same remote server work properly.
Havng the same issue i've done this class. I'm thinking better way do a getData, method. But it works.
http = require "http"
_ = require "underscore"
class Connect
constructor: (#login, #password, #host='127.0.0.1', #port=9091, #headers={}) ->
getData: (params)->
key = "x-transmission-session-id"
options = {
host: #host
port: #port
path: '/transmission/rpc',
method: 'POST',
headers: #headers || {},
auth: "#{ #login }:#{ #password }"
}
_.extend options, params || {}
req = http.request(options, (res)=>
if res.statusCode == 401
console.log "Auth errror"
else if res.statusCode == 409
auth_header={}
auth_header[key] = res.headers[key]
_.extend #headers, auth_header
#getData(params)
else if res.statusCode == 200
res.setEncoding 'utf8'
res.on('data', (chunk)->
#here should be an emmit of data
console.log chunk
)
else
console.log "Error #{ res.statusCode }"
)
req.write('data\n')
req.write('data\n')
req.end()
connector = new Connect "transmission", "password"
connector.getData()
Well, I was able to circumvent - but not solve - the problem, using mikeal's request, which also simplified my code. The most recent version looks like this:
runRemoteCommand: (params, callback = false) =>
options =
uri: #uri
method: "POST"
json: params
if #sessionId
options.headers =
"X-Transmission-Session-Id": #sessionId
request options, (error, response, body) =>
retVal =
success: false
end = true
if error
retVal.message = "An error occured: #{error}"
else
switch response.statusCode
when 409
if response.headers["x-transmission-session-id"]
#sessionId = response.headers["x-transmission-session-id"]
end = false
#.runRemoteCommand params, callback
else
retVal.message = "Session id not present"
when 200
retVal.success = true
retVal.response = body
else retVal.message = "Error, code: #{response.statusCode}"
callback retVal if end && callback
I'll leave this answer unaccepted for the time being because I still don't know what was wrong with the "raw" version.
#!/bin/bash
#-----------------------------------------------------------------------
#
DEBUG=0
HOST="192.168.1.65"
PORT="8181"
TRURL="http://$HOST:$PORT/transmission/rpc"
USER="admin"
PASSWORD="password1"
XTSID=""
#-------------------------------------
#
function getSID ()
{
local S="$1"
S=${S##*X-Transmission-Session-Id: }
S=${S%%</code>*}
echo $S
}
#-------------------------------------
function getData ()
{
local REQUEST="$1"
local RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
-H "Content-type: application/json" \
-X POST \
-d "$REQUEST" \
--user $USER:$PASSWORD $TRURL)
((DEBUG)) && echo $XTSID
if [[ "$RET" =~ "409: Conflict" ]]
then
XTSID=$(getSID "$RET")
((DEBUG)) && echo "XTSID $XTSID"
RET=$(curl --silent -H "X-Transmission-Session-Id: $XTSID" \
-H "Content-type: application/json" \
-X POST \
-d "$REQUEST" \
--user $USER:$PASSWORD $TRURL)
fi
echo $RET
}
#-------------------------------------
R='{"method":"session-stats"}'
RET=$(getData "$R")
echo $RET