I am sending an email from a Shiny app via the SendGrid API using the HTTR package (using POST function with json encoding). I need to pass an R object in between the json quotes that are used to define the text body of the email:
I have tried to convert the R object to json as follows:
client_id<- "f432jj"
email_text<- paste("Below is your unique key:", client_id, "Please copy
your key to the clipboard, then click 'Begin'")
email_text<- jsonlite::toJSON(email_text)
Here is the json code into which I need to insert the email_text object.
body = '{"from": {"email":"xxx#gmail.com"},
"personalizations": [{"to": [{"email":"zzz#gmail.com"}],
"dynamic_template_data":{
"header":"A measure is ready to be completed",
"text": email_text,
"c2a_button":"Begin",
"c2a_link":"yyy#gmail.com"}}],
"template_id":"e-98766334"}'
When trying to pass in the email_text object as above and send the email, I get:
HTTP/1.1 400 Bad Request
I think this means the syntax is wrong.
Any help much appreciated.
Normally you wouldn't build your body JSON data as a string. You could build the list that represents the data and then let jsonlite turn it into a JSON string for you. Your example might look something like this
client_id<- "f432jj"
email_text<- paste("Below is your unique key:", client_id, "Please copy
your key to the clipboard, then click 'Begin'")
body <- list(
from = list(email="xxx#gmail.com"),
personalizations = list(list(to=list(list(email="zzz#gmail.com")))),
dynamic_template_data = list(
header="A measure is ready to be completed",
text = email_text,
c2a_button = "Begin",
c2a_link = "yyy#gmail.com"
),
template_id = "e-98766334"
)
jsonlite::toJSON(body,auto_unbox=TRUE)
Related
Let say there is R code for REST API based on using the "plumber" package.
Here is a function from it.
#' Date of sale
#' #get /date_of_sale
#' #param auth_key Auth key
#' #param user_id User ID
#' #example https://company.com/date_of_sale?auth_key=12345&user_id=6789
function(auth_key, user_id) {
# ...
}
Let say there is another R script that uses API request to this server like
api_string <- "https://company.com/date_of_sale?auth_key=12345&user_id=6789"
date_of_sale <- jsonlite::fromJSON(api_string)
Is it possible to get a description of the parameters "auth_key" and "user_id" in the second script to have a full explanation of what means each parameter? For example, get for "auth_key" a string "Auth key"? Or how it will be possible to get access to function "date_of_sale" metadata at all?
Thanks for any ideas?
Using a file plumber.R with content as you provided. Assuming it is in the working directory.
In R
pr_read <- plumber::pr("plumber.R")
spec <- pr_read$getApiSpec()
spec$paths$`/date_of_sale`$get$parameters
spec is an R list with the same structure as an OpenAPI document.
If you do not have access to API plumber file but your API is running somewhere you have access to.
spec <- jsonlite::fromJSON("{api_server}/openapi.json", simplifyDataFrame = FALSE)
spec$paths$`/date_of_sale`$get$parameters
Again this follows the OpenAPI documentation standards.
Since this is an API GET request you can not access the description of the variable unless you explicitly include it in the API response.
I've learned some R script purely for this question, my guess is this is how you've prepared your JSON API response.
You can do something like this in your JSON request.
library(rjson)
auth_key <- "some_key";
user_id <- "340";
x <- list(auth_key = list(
type = typeof(auth_key),
lenght = length(auth_key),
attributes = attributes(auth_key),
string = auth_key
),
user_id = list(
type = typeof(user_id),
lenght = length(user_id),
attributes = attributes(user_id),
string = user_id
),
data = "your_data"
);
#x
json <- toJSON(x, indent=0, method="C" )
fromJSON( json )
You might want to look a these.
https://stat.ethz.ch/R-manual/R-devel/library/base/html/typeof.html
https://ramnathv.github.io/pycon2014-r/learn/structures.html https://rdrr.io/cran/rjson/man/toJSON.html https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/attributes
I am decoding form fields submitted via a HTTP POST request using request-toolbelt. I successfully instantiated a MultipartDecoder like described here. Now I would like to access the form fields by the name I have given them when sending the request.
I am able to get the name of a field like this
from requests_toolbelt.multipart import decoder
multipart_string = b"--ce560532019a77d83195f9e9873e16a1\r\nContent-Disposition: form-data; name=\"author\"\r\n\r\nJohn Smith\r\n--ce560532019a77d83195f9e9873e16a1\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example2.txt\"\r\nContent-Type: text/plain\r\nExpires: 0\r\n\r\nHello World\r\n--ce560532019a77d83195f9e9873e16a1--\r\n"
content_type = "multipart/form-data; boundary=ce560532019a77d83195f9e9873e16a1"
decoder = decoder.MultipartDecoder(multipart_string, content_type)
field_name = decoder.parts[0].headers[b'Content-Disposition'].decode().split(';')[1].split('=')[1]
But this seems quite wrong. What is the usual way to access the form field names?
I use it in the following to decode the result of that method:
lst = []
for part in decoder.MultipartDecoder(postdata.encode('utf-8'), content_type_header).parts:
disposition = part.headers[b'Content-Disposition']
params = {}
for dispPart in str(disposition).split(';'):
kv = dispPart.split('=', 2)
params[str(kv[0]).strip()] = str(kv[1]).strip('\"\'\t \r\n') if len(kv)>1 else str(kv[0]).strip()
type = part.headers[b'Content-Type'] if b'Content-Type' in part.headers else None
lst.append({'content': part.content, "type": type, "params": params})
I assume because its a standard Mime header, there are functions that can do the same, but with less code as well.
I am trying to pull a list of Coinbase accounts into R using their API. I receive an authentication error saying "invalid signature". Something is clearly wrong when I create my sha256 signature but I can't figure out what the issue is. I have not had this same issue when accessing the GDAX API using a sha256 signature.
API Key Documentation
API key is recommend if you only need to access your own account. All API >key requests must be signed and contain the following headers:
CB-ACCESS-KEY The api key as a string
CB-ACCESS-SIGN The user generated message signature (see below)
CB-ACCESS-TIMESTAMP A timestamp for your request
All request bodies should have content type application/json and be valid JSON.
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on the prehash string timestamp + method + requestPath + body (where + represents string concatenation). The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.
My Code
library(httr)
library(RCurl)
library(digest)
coinbase_url <- "https://api.coinbase.com"
coinbase_reqPath <- "/v2/accounts/"
coinbase_fullPath <- paste(coinbase_url, coinbase_reqPath,sep = "")
coinbase_key <- "XXXXMYKEYXXX"
coinbase_secret <- "XXXXXMYSECRETKEYXXXX"
cb_timestamp <- format(as.numeric(Sys.time()), digits=10)
coinbase_message <- paste0(cb_timestamp,"GET", coinbase_reqPath)
coinbase_sig <- hmac(key = coinbase_secret, object = coinbase_message, algo = "sha256", raw = F)
coinbase_acct <- content(GET(coinbase_fullPath,
add_headers(
"CB-ACCESS-KEY" = coinbase_key,
"CB-ACCESS-SIGN" = coinbase_sig,
"CB-ACCESS-TIMESTAMP" = cb_timestamp,
"Content-Type"="application/json")))
Sorry for not updating this earlier. The answer is simple, I just needed to remove the final forward slash in "/v2/accounts/" when specifying my request path.
I am struggling to Sign a XML soap message with a private key . I have done it in Java in the past , but having a very hard time in doing it through Python. I have kept a template XML in home directory , with values filled up for "BinarySecurityToken" and "KeyInfo" tags . The values in these are generated through SOAP UI using the same private key (as the URI that points to the Body tag is always same) . After that I am calculating the digest value of the whole Body tag and populating that in "DigestValue" tag in "SignedInfo" . Now I am canonlizing this Signed Info tag and calculating the "SignatureValue" over it . But ultimately , when I am passing this Soap XML to the webservice , I am getting a policy faliure message (because of wrong Signature generaion ) , below is my code :
body = etree.tostring(root.find('.//{http://schemas.xmlsoap.org/soap/envelope/}Body'))
c14n_exc = True
ref_xml = canonicalize(body, c14n_exc)
digest_value = sha1_hash_digest(ref_xml)
#Inserting the digest Value
for soapheader in root.xpath('soapenv:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference', namespaces=ns):
soaptag = etree.XPathEvaluator(soapheader,namespaces=ns)
soaptag('ds:DigestValue')[0].text = digest_value
signed_info_xml = etree.tostring(root.find('.//{http://www.w3.org/2000/09/xmldsig#}SignedInfo'))
signed_info = canonicalize(signed_info_xml, c14n_exc)
pkey = RSA.load_key("privkeyifind.pem", lambda *args, **kwargs: "nopass")
signature = pkey.sign(hashlib.sha1(signed_info).digest())
signature_value = base64.b64encode(signature)
#Inserting the signature Value
for signedInfo in root.xpath('soapenv:Header/wsse:Security/ds:Signature', namespaces=ns):
signtag = etree.XPathEvaluator(signedInfo,namespaces=ns)
signtag('ds:SignatureValue')[0].text = signature_value
canonReq = canonicalize(etree.tostring(root), c14n_exc)
proc = Popen(["curl", "-k", "-s" ,"--connect-timeout", '3', '--data-binary' , canon2, "https://world-service-dev.intra.aexp.com:4414/worldservice/CLIC/CaseManagementService/V1"], stdout=PIPE, stderr=PIPE)
response, err = proc.communicate()
#######################################################
#Method to generate the digest value of the xml message
#######################################################
def sha1_hash_digest(payload):
"Create a SHA1 hash and return the base64 string"
return base64.b64encode(hashlib.sha1(payload).digest())
#####################################
#Method to canonicalize a request XML
#to remove tabs, line feeds/spaces,
#quoting, attribute ordering and form
#a proper XML
#####################################
def canonicalize(xml, c14n_exc=True):
"Return the canonical (c14n) form of the xml document for hashing"
# UTF8, normalization of line feeds/spaces, quoting, attribute ordering...
output = StringIO()
# use faster libxml2 / lxml canonicalization function if available
et = lxml.etree.parse(StringIO(xml))
et.write_c14n(output, exclusive=c14n_exc)
return output.getvalue()
I can only use standard Python function of 2.6.6 . I can not download message signing lib like signxml etc (due to the restriction on the environment).
I'm trying to access the US Census Geocoder batch address API, found here: http://geocoding.geo.census.gov/geocoder/
I've also gone through the documentation for the API here: http://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.pdf
I'm trying to use the httr package in R to post a batch csv file of formatted addresses using this format: Unique ID, Street address, City, State, ZIP
I've tried the single address request version using getURL from RCurl and that works fine, but postForm doesn't seem to submit the file in the right way. The code I'm using right now seems to post the request correctly, but I'm not getting any geocoded data back.
curlhandle <- handle("http://geocoding.geo.census.gov/geocoder/geographies/addressbatch",
cookies = TRUE)
# using fileUpload from RCurl instead of upload_file from httr
upload3 <- fileUpload(contents = address100, contentType = "file")
test <- POST(url="http://geocoding.geo.census.gov/geocoder/geographies/addressbatch",
body = list(addressFile = upload3,
benchmark = "Public_AR_Census2010",
vintage="Census2010_Census2010"),
encode = "multipart",
handle = curlhandle,
followLocation = TRUE,
verbose = TRUE)
Is my request missing something? I'm not sure if I should be using writefunction & writedata in this case. Any help would be appreciated!
You seem to have a weird mix of RCurl and httr. Here's how I'd write the request:
req <- POST(
"http://geocoding.geo.census.gov/geocoder/geographies/addressbatch",
body = list(
addressFile = upload_file(address100),
benchmark = "Public_AR_Census2010",
vintage = "Census2010_Census2010"
),
encode = "multipart",
verbose())
stop_for_status(req)
content(req)
(also "file" is not a valid mime type)