I am trying to use the Invoke-RestMethod to upload an attachment to a Smartsheet using the API.
This is the curl example from the Smartsheet API Documentation:
curl https://api.smartsheet.com/2.0/sheets/{sheetId}/attachments \
-H "Authorization: Bearer ll352u9jujauoqz4gstvsae05" \
-H "Content-Type: application/msword" \
-H 'Content-Disposition: attachment; filename="ProgressReport.docx"' \
-H "Content-Length: FILE_SIZE" \
-X POST \
--data-binary #ProgressReport.docx
This is my Powershell code.
$BaseURI = "https://api.smartsheet.com/2.0"
$Id = {sheet id} #sheet id passed a parameter
$Uri = "{0}/sheets/{1}/attachments" -f $BaseUri, $id
$mimetype = ""application/vnd.openxmlformats-officedocument.wordprocessingml.document"
$FileName = "MyDocument.docx"
$Config = Read-Config #reads authentication token from text file"
$token = ConvertTo-SecureString -String $config.APIKey -AsPlainText -Force
$response = Invoke-RestMethod -Method POST -Uri $Uri -InFile $FileName -ContentType $mimetype -Authentication:Bearer -token $token
What I get is this response:
{ "errorCode" : 1008, "message" : "Unable to parse request. The following error occurred: Request body must be either a JSON
| object or JSON array.", "refId" : "12y60zzovzndm" }
If I try to post a text file with just the work "test" in it I get this error.
{ "errorCode" : 1008, "message" : "Unable to parse request. The following error occurred: Unrecognized token 'test': was expecting 'null', 'true', 'false' or NaN\n at [Source: REST input; line: 1, column: 6]", "refId" : "lbstw0mq73l9" }
If I do this API call in Postman it works fine.
I know there is a C# SDK but I'm building a PowerShell module and I don't want those .dll files as a requirement for this module.
OK I got this to work thanks to Postman.
I build the headers as a hashtable"
Name Value
---- -----
Content-Type application/vnd.openxmlformats-officedocument.wordprocessingml.document
Authorization Bearer VcjJ....
Content-Disposition attachment; filename="CWilliams.docx"
Convert the input file into a byte array with:
$body = [System.IO.File]::ReadAllBytes($path)
Then issue the statement:
$response = Invoke-RestMethod -Method 'POST' -Uri $Uri -Headers $Headers -Body $body
From what I understand about the Invoke-RestMethod function the -inFile parameter is supposed to handle all this for you but it doesn't work with the Smartsheet API.
Related
I am using Powershell 7.3.1 and Invoke-RestMethod. I need to submit a form to a server but one of the fields needs to be typed as JSON or the request will fail. In cURL, this can be accomplished by appending ;type=application/json to the field but that doesn't seem to work with Invoke-RestMethod.
In cURL, submitting a form with types would look something like this:
curl --location --request POST 'http://localhost:8082/csv-as-input' \
--header 'Content-Type: multipart/form-data' \
--form 'file=#"C:/Users/test/task-b5cfe19-input.csv"' \
--form 'json="{\"name\": \"Test\",\"jobType\": \"BATCH_FILE\",\"runMode\": \"VALIDATE\",\"config\": {\"branchId\": \"12345\",\"trustedSource\": true}}";type=application/json'
In this case, the json form element is treated as a JSON string because of the ;type=application/json at the end.
I have tried to write the Powershell version using the new -Form parameter:
$CreateJobForm = #{
'file' = Get-Item -Path 'C:\Users\test\task-b5cfe19-input.csv'
'json' = '{"name": "Test","jobType": "BATCH_FILE","runMode": "VALIDATE","config": {"branchId": "12345","trustedSource": true}};type=application/json'
}
Invoke-RestMethod -Method Post -Uri http://localhost:8082/csv-as-input -Form $CreateJobForm
The cURL command works but the Powershell version fails because it is coming across as a text/plain type.
Is there a way to type the json field as JSON in Powershell?
You can use the -ContentType parameter to specify the content type of the request body as multipart/form-data, and then use the ConvertTo-Json cmdlet to convert the JSON object to a JSON string and include it in the form data.
$json = #{
'name' = 'Test'
'jobType' = 'BATCH_FILE'
'runMode' = 'VALIDATE'
'config' = #{
'branchId' = '12345'
'trustedSource' = $true
}
}
$CreateJobForm = #{
'file' = Get-Item -Path 'C:\Users\test\task-b5cfe19-input.csv'
'json' = (ConvertTo-Json -InputObject $json)
}
Invoke-RestMethod -Method Post -Uri http://localhost:8082/csv-as-input -ContentType 'multipart/form-data' -Form $CreateJobForm
I am attempting to run a cURL command in PowerShell using the Invoke-RestMethod cmdlet but it will not work properly.
It connects to the server and the API key is accepted. However, the credentials are not being passed correctly and I am receiving a response of
{"error":"Invalid user . Can't find credentials."}
When I run the following cURL command in cmd:
curl -X POST "https://valid_URI" -H "Content-Type: application/json" -H "x-api-key:KEY" -d "{"client_id":"VALID_ID","client_secret":"SECRET"}"
I get the expected response:
{"auth_token": "TOKEN"}
Great, it works!
From here I wanted to automate the response because a token is only good for 24 hours so I figured PowerShell would be a good route using the Invoke-RestMethod cmdlet.
So I try to run:
$Headers = #{
'x-api-key' = 'KEY'
'client_id' = 'VALID_ID'
'client_secret' = 'SECRET'
}
$Parameters = #{
Uri = 'VALID_URI'
Method = 'Post'
Headers = $Headers
ContentType = 'application/json'
}
Invoke-RestMethod #Parameters
This give the following output:
Invoke-RestMethod : {"error":"Invalid user . Can't find credentials."}
At line:13 char:1
+ Invoke-RestMethod #Parameters
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I have tried a few different variations on this as well as changing up the quotes to no avail.
I have a suspicion that the secret which contains "==" is getting chopped up for some reason but using single quotes should have negated that as PowerShell should interpret anything in single quotes as a literal right?
To mimick the curl command listed:
curl -X POST "https://valid_URI" -H "Content-Type: application/json" -H "x-api-key:KEY" -d "{"client_id":"VALID_ID","client_secret":"SECRET"}"
You would need to include the client_id and client_secret in the body rather than the Header.
$Headers = #{
'x-api-key' = 'KEY'
}
$Body = #{
'client_id' = 'VALID_ID'
'client_secret' = 'SECRET'
} | ConvertTo-Json
$Parameters = #{
Uri = 'VALID_URI'
Method = 'Post'
Headers = $Headers
Body = $Body
ContentType = 'application/json'
}
Invoke-RestMethod #Parameters
Invoke-RestMethod -Method Post -Uri "VALID_URI" -Headers #{"Content-Type" = "application/json"; "x-api-key" = $API_KEY} -Body (ConvertTo-Json #{"client_id" = "VALID_ID"; "client_secret" = "SECRET"})
This is what ended up working. I wasn't too far off and thank you everyone for helping!
This curl command works:
curl -v -X POST https://subdomain.playvox.com/api/v1/files/upload?context=quality -H "Content-Type: multipart/form-data" -u [username]:[password] -F file=#c:\path\to\file.wav
But I am unable to perform the same thing in PowerShell using the Invoke-RestMethod cmdlet. Here's my attempt:
$file_contents = [System.IO.File]::ReadAllBytes($($file_path))
Invoke-RestMethod -Uri "https://subdomains.playvox.com/api/v1/files/upload?context=quality" -Method Post -ContentType "multipart/form-data" -Headers #{ "Authorization" = "Basic $($playvox_base64_auth)" } -Body #{ file = $file_contents }
When run the API responds with invalid_param, "file" is required. However I confirmed the call to ReadAllBytes succeeds and gives back the raw file data. It seems like PowerShell is not sending the request body in the right format? I've looked at several other answers here and documentation online and nothing I found has worked.
Discovered there is a -Form parameter in Powershell 6 and later. I updated my powershell and used the following instead:
$file_contents = Get-Item $file_path
Invoke-RestMethod -Uri "https://subdomains.playvox.com/api/v1/files/upload?context=quality" -Method Post -ContentType "multipart/form-data" -Headers #{ "Authorization" = "Basic $($playvox_base64_auth)" } -Form #{ file = $file_contents }
And it worked.
I have follwoing curl command which I would like to convert to PowerShell (PowerShell v3.0):
curl -s -k -H 'Authorization: api_key 1234567890' -F upload_file=#"C:\path\to\file" -X POST "https://my.url.com/submit"
What I have so far:
$Body= #{ "upload_file" = "C:\path\to\file" };
$Headers = #{ "Authorization" = "api_key 1234567890" };
$Uri = "https://my.url.com/submit"
Invoke-WebRequest -Method Post -Uri $Uri -H $Headers -Body $Body
I think the form parameter is the issue.
Thanks in advance.
I have a requirement to convert a working curl command to Invoke-WebRequest command
The command is used to create a project in SonarQube
Curl:
curl -u as123123112312: -X POST "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%" > NUL
the command I tried:
$e = #{
Uri = "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%"
Headers = #{"Authorization" = "Token as123123112312"}
}
Invoke-WebRequest #e -Method POST
Error:
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized
Does anyone have an idea of converting curl to invoke-webrequest
This has been asked before. When you are posting, you need to have a body to send too, example:
$username = "as123123112312";
$password = "blah";
$Bytes = [System.Text.Encoding]::UTF8.GetBytes("$username`:$password");
$encodedCredentials = [System.Convert]::ToBase64String($bytes);
$body = "your content (i.e. json here)";
Invoke-WebRequest -Method Post -Body $body -Headers #{ "Authorization" = "Basic " + $encodedCredentials} -Uri "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%"