cURL to PowerShell - Value in Square Bracket - powershell

I've been struggling with another cURL to PowerShell conversion, specifically with a value in a square bracket.
Background: I am about 1,500 lines into a script that takes CSV import files, consumes it, validates it then converts it into a series of API calls, to automate a process which is done manually. I am doing this as a learning opportunity for myself, as I have never done API work before.
I have this cURL command (from Postman):
curl -X PATCH \
https://example.com \
-H 'Authorization: Bearer 1234567890' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"CID": 1234,
"GroupId": [
31
]
}
}'
My struggle is with the square brackets round the "GroupId".
To convert it to PowerShell, I have done the following:
$URL = "https://example.com"
$Body = #{
'data' = #{
'CID' = $CustomerID;
'GroupId' = $GetGroupID2
}
}
$CurlArgument = '-X', 'PATCH',
'-H', 'Content-Type: application/json',
$URL1,
'-H',
$AuthBearer,
'--retry', '2',
'--retry-delay', '3',
'--retry-connrefused',
'-d',
(($Body | ConvertTo-Json) -replace '"', '\"')
Write-Host "$Section cURL command took" ("{0:n1}" -f (Measure-Command {$PatchGroupResponse = & $CURLEXE #CurlArgument}).TotalSeconds) "Seconds" -ErrorAction SilentlyContinue
Now, PowerShell "hates" the square brackets enclosing "GroupID", so I did the following (Let say $GetGroupID contains 31 (with no quotes. I get this earlier in my script and save it to the variable):
$GetGroupID2 = '['+$GetGroupID+']'
$GetGroupID2 now contains: [31], however when I ConvertTo-Json, I get the extra quotes round my $GetGroupID2, [31] becomes "[31]", I assume from the -replace command but I don't know why/how or how to stop it.
My #CurlArgument looks like this:
-X
PATCH
-H
Content-Type: application/json
https://example.com
-H
Authorization: Bearer 1234567890
--retry
2
--retry-delay
3
--retry-connrefused
-d
{
\"data\": {
\"CID\": 1234,
\"GroupId\": \"[31]\"
}
My call to the server is failing with:
{"Missing Argument, Argument Name = CID"}.
From what I can see, everything looks the same as my cURL command, except the extra quotes round my GroupID of "[31]" (should be [31]).
Can anyone give me any pointers as to why the extra quotes appear round the [31]?

By doing this: $GetGroupID2 = '['+$GetGroupID+']' you are creating a string [31] and therefore the value gets quoted by the ConvertTo-Json cmdlet.
In fact, it should be output as the single element of an array, so if you do this instead:
'GroupId' = #($GetGroupID2)
you are forcing the GroupId value to be an array, so the output will be like this:
"data": {
"CID": 1234,
"GroupId": [
31
]
}

Related

CURL example to Invoke-RestMethod

I am trying to convert curl command to powershell Invoke-RestMethod
Command from MobileIron (in case someone will google that topic)
curl --user <username>:<password> --header 'Content-Type: application/json' --request PUT 'https://
<mobileiron_core>/api/v2/devices/wakeup?adminDeviceSpaceId=1' --data '{"deviceUuids": ["af0cea8bf56c-
49f5-b615-76cc34936b2f"], "note": "something"}'
Ok, my code (based on https://gist.github.com/rayterrill/e83a1bd877547eccfd59b656e7f91b48#file-mobileiron-addremovelabels-ps1 )
$url = $mi_server + "/api/v2/devices/wakeup?adminDeviceSpaceId=1"
$contentType = "application/json"
$headers = #{
Authorization=("Basic {0}" -f $regase64AuthInfo)
}
$data = #{
note = "test";
deviceUuids = $guid
};
$json = $data | ConvertTo-Json;
Invoke-RestMethod -Method PUT -Uri $url -ContentType $contentType -Headers $headers -Body $data;
But i am getting 400 error - so,as i understand - something wrong with --data , but what ?
MobileIron 400 is a general error: 400 Bad request, Meaning the request was invalid.
The accompanying error message (That follows the the error code) explains the reason.
Can you post the rest of the error message?
You cannot use --data or -Body with a PUT request.
You must make a query string for the data.
The query string must be url encoded.
Because it is HTTPS transfer you may need -k (--insecure)
The PUT request may work, I believe MobileIron prefers GET, There is no real difference between the two. I tested both.
I tried (I did my testing with PHP)
A PUT Request
Request Headers
Content-Type: application/json
Accept: application/json
POST DATA was the same as the Query String
I checked with and without the POST DATA and no change.
I also sent the POST DATA with no query string and it was if not data was sent.
URL and Query
$query = urlencode('{"deviceUuids": ["af0cea8bf56c-49f5-b615-76cc34936b2f"], "note": "something"}');
$url = https://<domain>/api/v2/devices/wakeup? . $query
URL was changed to my server to an app that returns the request header, and parameters.
Which returned this on the server:
Request Headers:
Content-Type: application/json
Accept: application/json
Accept-Encoding: deflate, gzip, br
Expect: 100-continue
Host: mydomain.com
Transfer-Encoding: chunked
The server GET and server REQUEST values
$_GET
array (
'{"deviceUuids":_' =>
array (
'"af0cea8bf56c-49f5-b615-76cc34936b2f"' => '',
),
)
$_REQUEST
array (
'{"deviceUuids":_' =>
array (
'"af0cea8bf56c-49f5-b615-76cc34936b2f"' => '',
),
)
The Request Query
$_SERVER['QUERY_STRING']) (url decoded)
{"deviceUuids": ["af0cea8bf56c-49f5-b615-76cc34936b2f"], "note": "something"}
Notice above the JSON query string was cut off.
But was in the arguments
argv array (url decoded)
0 = {"deviceUuids":
1 = ["af0cea8bf56c-49f5-b615-76cc34936b2f"],
2 = "note":
3 = "something"}
I removed the Content-Type: application/json from the request header and there was no change.
I changed the PUT to GET, no change
When a I started sending the Content=Type: application/json
With a POST request, and no Query String, the POS Data was ONLY in the BODY of the request.
And here are some example requesst from some MobileIron documentation:
The first request was a default GET not a PUT.
curl -u admin_username:admin_password -kv "https://URL_to_your_mobileiron_cloud_environment/api/v1/account?emailAddress=johnnydoe#acme.com&uid=johnnydoe#acme.com&accountSource=Local" -d ''
curl GET -kv -u user:password "https://[mobileiron_cloud]/api/v1/device/57d9903c-aadf-4b40-aef3-e1f24302f180/customattributes”
curl -X POST -kv -u user:password -H 'Content-Type: application/json' -d '{ "attrs" : { "tlark" : [ "1" ] } } ' "https://[mobileiron_cloud]/api/v1/device/22000/customattributes"
curl -X POST -kv -u user:password -H 'Content-Type: application/json' -d '{ "attrs" : { "tlark" : [ "1" ] } } ' "https://[mobileiron_cloud]/api/v1/device/8bcc4cee-dca9-476d-8710-9bb1e738ade9/customattributes"
$param_pam_pam = #{
Method = 'PUT'
Uri = "$url"
ContentType = "application/json"
Headers = #{authorization = ("Basic {0}" -f $regase64AuthInfo) }
Body = #{
deviceUuids = #($guid)
note = "force check-in from $ma"
} | ConvertTo-Json
}
$reg = Invoke-RestMethod #param_pam_pam

Perl: LWP::UserAgent Regarding rules for passing parameters

e.g.
I have a command
curl -H "Content-Type: application/json" -H "Authorization: Bearer ACCESS_TOKEN" -X GET https://api.xxx/v1/datapackages/yyy
(1) I am confused when to use [ ] while passing the parameters. which one below is correct and why?
without [ ]
my $r2 = $ua -> get("https://api.xxx/v1/datapackages/yyy",
'Content-Type' => 'application/json',
Authorization => 'Bearer '.$token,
);
or with [ ]
my $r2 = $ua -> get("https://api.xxx/v1/datapackages/yyy",
[
'Content-Type' => 'application/json',
Authorization => 'Bearer '.$token,
]
);
(2) I see sometimes the parameter is quoted and sometimes not. Should "Authorization" be quoted and write as
with ' '
'Authorization' => 'Bearer '.$token,
or without ' '
Authorization => 'Bearer '.$token,
Thanks a lot!
The documentation for get() says this:
get
my $res = $ua->get( $url );
my $res = $ua->get( $url , $field_name => $value, ... );
This method will dispatch a GET request on the given URL. Further arguments can be given to initialize the headers of the request. These are given as separate name/value pairs. The return value is a response object. See HTTP::Response for a description of the interface it provides.
So, that just talks about passing arguments as key/value pairs. It says nothing about using an array reference. So, if I were you, I'd stick with the first approach.
The "fat comma" (=>) is documented in perldoc perlop (because it's an operator) which says:
The => operator (sometimes pronounced "fat comma") is a synonym for the comma except that it causes a word on its left to be interpreted as a string if it begins with a letter or underscore and is composed only of letters, digits and underscores. This includes operands that might otherwise be interpreted as operators, constants, single number v-strings or function calls. If in doubt about this behavior, the left operand can be quoted explicitly.
Otherwise, the => operator behaves exactly as the comma operator or list argument separator, according to context.
Basically, if the thing on its left is a single word (by which I mean a sequence of alphanumeric characters and underscores), then you can omit the quote marks around it.

Silex - My REST POST/PUT calls aren't receiving parameters

I'm currently using the Doctrine DBAL version 2.5 inside of Silex 1.3 to make REST calls to MySQL DB. I manage to get my GET / GET/{id} and DELETE/{id} working fairly easily but I can't seem to get my POST/PUT to work.
Here is the fallowing code
$app->post('/student', function($firstName, $lastName) use($app) {
$sql = "INSERT INTO tbl_students (firstName, lastName)
VALUES (:firstName, :lastName)";
$student = $app['db']->prepare($sql);
$student->bindValue(':firstName', $firstName, PDO::PARAM_STR);
$student->bindValue(':lastName', $lastName, PDO::PARAM_STR);
$student->execute();
return "insert successful";
});
$app->put('/student/{id}', function($id, $firstName, $lastName) use($app){
$sql = "UPDATE tbl_students
SET firstName = :firstName, lastName = :lastName
WHERE id = :id";
$student = $app['db']->prepare($sql);
$student->bindValue(':id', $id, PDO::PARAM_INT);
$student->bindValue(':firstName', $firstName, PDO::PARAM_STR);
$student->bindValue(':lastName', $lastName, PDO::PARAM_STR);
$student->execute();
return "student succesfully updated";
});
If I hard code default values, it works perfectly fine as well as using bindParam() and using my own values.
I keep getting this error message after my curl dumps everything back at me
<span class="exception_message">Controller "Closure" requires that you provide a value for the "$firstName" argument (because there is no default value or because there is a non optional argument after this one).</span>
Here are the fallowing curl commands I have used
curl -X POST -d "firstName=First123&lastName=Last456" URL/student
curl -X POST -d '{"firstName":"First123","lastName":"Last456"}' URL/student --header "Content-Type:application/json"
curl -X POST -d "firstName=First123&lastName=Last456" URL/student --header "Content-Type:text/html"
Silex will not automatically create parameters for your methods corresponding to your post variables. You have to get them from the request object as #Artamiel suggested.
See http://silex.sensiolabs.org/doc/usage.html#example-post-route.
The updated code for your post operation should be like this:
$app->post('/student', function(Request $request) use($app) {
$firstName = $request->get('firstName');
$lastName = $request->get('lastName');
$sql = "INSERT INTO tbl_students (firstName, lastName)
VALUES (:firstName, :lastName)";
$student = $app['db']->prepare($sql);
$student->bindValue(':firstName', $firstName, PDO::PARAM_STR);
$student->bindValue(':lastName', $lastName, PDO::PARAM_STR);
$student->execute();
return "insert successful";
});

LWP::UserAgent SendHub API Issue

SendHub's documentation state to send a post with data and json data.
This is the curl example they gave
Trying to do this with perl and LWP::User Agent but getting either bad request or unauthorized
Do I have the request coded properly?
curl -H "Content-Type: application/json" -X POST --data '{"contacts" : [1111],"text" : "Testing"}' https://api.sendhub.com/v1/messages/?username
require LWP::UserAgent;
my $uri = 'https://api.sendhub.com/v1/messages/?username=MY_USERNAME\&api_key=MY_KEY_NUMBER';
my $json = '{"contacts":[18005551212],"text":"testing"}';
my $req = HTTP::Request->new('POST',$uri);
$req->header('Content-Type' => 'application/json');
$req->content($json);
my $lwp = LWP::UserAgent->new;
my $response=$lwp->request($req);
if ($response->is_success) {
print $response->decoded_content;
}
else {
die $response->status_line;
}
It looks basically OK.
What is the backslash before the ampersand in the URL?
'https://api.sendhub.com/v1/messages/?username=MY_USERNAME\&api_key=MY_KEY_NUMBER'
I think it should be
'https://api.sendhub.com/v1/messages/?username=MY_USERNAME&api_key=MY_KEY_NUMBER'
but if you're getting 401 Unauthorized then it's most likely the request is correct but the user name and key are wrong.

Dancer : how to share data and waiting response between 2 POST without blocking

I have a rest server written in perl dancer on a VM, this is the workflow :
customers POST some json stuff in /post_wait route : the POST should waiting the business processing to return the result
the request is processed by another VMs (communication is processed by POE + IKC)
the report should be returned by /report route to the customer identified with the session id by the REST server
DANCER CODE : (that's what I tried so far)
package var;
use Data::Dumper;
use Dancer ':syntax';
use JSON::XS;
my $session = {};
# curl -H 'Content-Type: application/json' -X POST -d '{"id":22}' http://127.0.0.1:3000/post_wait
post '/post_wait' => sub {
my $json = request->body;
my $h = decode_json $json;
my $id = $h->{id};
until (exists $session->{$id}->{report}) {
sleep 1;
print STDERR ".";
}
return Dumper $session;
};
# curl -H 'Content-Type: application/json' -X POST -d '{"foobar":"xxxx"}' http://127.0.0.1:3000/report
post '/report' => sub {
my $json = request->body;
my $h = decode_json $json;
my $id = $h->{id};
$session->{$id}->{report} = $h;
return "OK\n";
};
true;
The problem is that this code hangs, because I sleep the parent process.
Maybe I should use :
rewrite that with POE-Component-Server-REST ?
Dancer::Plugin::Async module ? but can I avoid using this ? (there's some technical restrictions)...
another script as a subprocess ?
a database like redis queue using BLPOP to dequeue ? (that needs some subprocess code around)
Dancer::Session ?
fork() ?
Threads ? (I guess no)
My information system should be scalable and in high-availability mode.
What is the best practice to do what I want ?
You need some kind of seesion tracking. Use Dancer::Session for that.
And you need to do some 'asynchronus', may be fork().
Then you can start the business process by calling
# curl -H 'Content-Type: application/json' -X POST -d '{"id":22}' http://127.0.0.1:3000/post_wait
where the process is forked and the pid is stored in the session.
Then you can call
# curl -H 'Content-Type: application/json' -X POST -d '{"foobar":"xxxx"}' http://127.0.0.1:3000/report
get the pid from your session (if you have one) and check if the process is still running or not.