with the following code I'm trying to create new conversation post for Capability. But it says
Cannot parse input stream due to I/O error as JSON document: Parse error: expected '{' but saw '' [ chars read = >>><<< ]
function createPost(objId, post) {
objId = "313878829904";
post = "<p>MindMap:Hello from GAS.</p>"
var url = "https://rally1.rallydev.com/slm/webservice/v2.0/conversationpost/create";
var payload = {
"ConversationPost": {
"Artifact": "/portfolioitem/capability/" + objId,
"Text": post
}
}
var method = "POST";
var options = optionsPost_(method, payload);
var response = UrlFetchApp.fetch(url, optionsPost_(method, options));
var content = JSON.parse(response.getContentText());
content.CreateResult.Errors.forEach(error => Logger.log(error));
}
function optionsPost_(method, payload) {
var rallyApiKey = "";
if (rallyApiKey != "") {
PropertiesService.getScriptProperties().setProperty("RallyApiKey", rallyApiKey);
} else {
rallyApiKey = PropertiesService.getScriptProperties().getProperty("RallyApiKey");
}
if (rallyApiKey == null) return null;
return {
headers: { "ZSESSIONID": rallyApiKey },
payload: payload,
method: method
};
}
I can't spot any problem.
Could you please help?
Thank you!
Petr
I thought that from your error message, the payload might be required to be sent as JSON data. If my guessing is correct, how about the following modification?
Modified script:
From:
return {
headers: { "ZSESSIONID": rallyApiKey },
payload: payload,
method: method
};
To:
return {
headers: { "ZSESSIONID": rallyApiKey },
payload: JSON.stringify(payload),
method: method,
contentType: "application/json"
};
Note:
In this modification, it supposes that the values of payload and rallyApiKey are valid values for using the API. Please be careful this.
When above modification was not the dierct solution of your issue, can you provide the official document of API you want to use? By this, I would like to confirm it.
Reference:
fetch(url, params) of Class UrlFetchApp
Thanks for the fast response.
With the following
var payload = {"ConversationPost":{"Artifact": "/portfolioitem/capability/"+objId,"Text": post}};
var method = "POST";
var options = optionsPost_(method, payload);
and
var options={
headers: { "ZSESSIONID": rallyApiKey },
payload: payload,
method: method,
contentType:"application/json"
};
It gives me
Cannot parse input stream due to I/O error as JSON document: Parse error: expected '{' but saw 'h' [ chars read = >>>h<<< ]
If I change it to
var options={
headers: { "ZSESSIONID": rallyApiKey },
payload: JSON.stringify(payload),
method: method,
contentType:"application/json"
};
It gives me
Cannot parse input stream due to I/O error as JSON document: Parse error: expected '}' but saw ',' [ chars read = >>>{"headers":{"ZSESSIONID":"_ycHaCSd2QZSf8kbkQ0R1yhjohUvSzUYas0caApHt2A"},<<< ]
Only documentation I use is this:
https://rally1.rallydev.com/slm/doc/webservice/objectModel.sp#ConversationPost
I can't find any difference but this actually started working.
I believe the problem was caused by combination of mistakes. I removed one problem but perhaps add another one.
Here is a code which works.
function createPost(objId, post) {
objId = '313878829908';
post = "<p>MindMap:Hello from GAS.</p>"
var url = "https://rally1.rallydev.com/slm/webservice/v2.0/conversationpost/create";
var payload = {'ConversationPost':{'Artifact': '/portfolioitem/capability/'+objId,'Text': post}};
var method = 'POST';
//var options = optionsPost_(method, payload);
var response = UrlFetchApp.fetch(url, optionsPost_(method, payload));
var content = JSON.parse(response.getContentText());
content.CreateResult.Errors.forEach(error => Logger.log(error));
}
function optionsPost_(method, payload) {
var rallyApiKey = "";
if (rallyApiKey != "") {
PropertiesService.getScriptProperties().setProperty("RallyApiKey", rallyApiKey);
} else {
rallyApiKey = PropertiesService.getScriptProperties().getProperty("RallyApiKey");
}
if (rallyApiKey == null) return null;
var options={
'headers': {'ZSESSIONID': rallyApiKey },
'payload': JSON.stringify(payload),
'method': method,
'contentType':'application/json'
};
return options;
}
Thanks Tanaike for your help. I really appreciate it.
Related
I am trying to create a script to post a text message in a chat on Guilded, but so far I get a "POST is not allowed"
Message should be "inPrivate" so a mention of the targeted member is required.
Google sheet test file
my script:
function message () {
var url = "https://www.guilded.gg/api/v1/channels/";
var channelid = "d2ba*****";
var key = "Bearer gapi_*****"; //Q-P API temp-bot
const message = {
content : "It's alive!",
embeds :[{
author:"API",
mentions:"Raven_test",
}],
isPrivate : true,
}
const params = {
method: "POST",
headers: { Authorization: key },
contentType: "application/json",
payload: JSON.stringify(message),
muteHttpExceptions: true,
};
Logger.log(JSON.stringify(params));
const response = UrlFetchApp.fetch(url + channelid + "/messages/", params);
var data = JSON.parse(response);
Logger.log(data);
}
Guilded's API
I don't understand what I'm doing wrong (but I'm a newbie, so it makes sens)
POST is still not working, with the same answer, however, if I use the PUT method, I can update a message if written by the same bot (I just need to add the messageid in the url after /messages/
function postmessage () {
var url = "https://www.guilded.gg/api/v1/channels/";
var channelid = "d2bac803-****-****-****-a609ab9c58c4";
var key = "Bearer gapi_******"; //Q-P API temp-bot
var server = "wlVKV***"
var messageid = "ae6f1693-****-****-****-c9984d4142de";
var author = "****";
const message = {
"type": "default",
"serverId":server,
"channelId":channelid,
"content" : "test #Aaron Raven ",
"mentions":{
"users" : [{"id":author}]
},
"isPrivate":false,
"isSilent":false
};
const params = {
method: "put",
headers: { Authorization: key },
contentType: "application/json",
payload: JSON.stringify(message),
muteHttpExceptions: true,
};
Logger.log(JSON.stringify(params));
// const response = UrlFetchApp.fetch(url + channelid + "/messages/" + messageid, params);
const response = UrlFetchApp.fetch(url + channelid + "/messages/" params);
var data = JSON.parse(response);
Logger.log(response);
Logger.log(data);
Logger.log(params);
}
Another issue (aside POST method) is that "mentions" doesn't work; it appears as basic text, and not as a user tag/link
Kind regards,
I'm trying to verify a HMAC signature received from a WebHook. The details of the WebHook are https://cloudconvert.com/api/v2/webhooks#webhooks-events
This says that the HMAC is generated using hash_hmac (PHP) and is a SHA256 hash of the body - which is JSON. An example received is:
c4faebbfb4e81db293801604d0565cf9701d9e896cae588d73ddfef3671e97d7
This looks like lowercase hexits.
I'm trying to use Cloudflare Workers to process the request, however I can't verify the hash. My code is below:
const encoder = new TextEncoder()
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const contentType = request.headers.get('content-type') || ''
const signature = request.headers.get('CloudConvert-Signature')
let data
await S.put('HEADER', signature)
if (contentType.includes('application/json')) {
data = await request.json()
await S.put('EVENT', data.event)
await S.put('TAG', data.job.tag)
await S.put('JSON', JSON.stringify(data))
}
const key2 = await crypto.subtle.importKey(
'raw',
encoder.encode(CCSigningKey2),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signed2 = await crypto.subtle.sign(
'HMAC',
key2,
encoder.encode(JSON.stringify(data))
)
await S.put('V22', btoa(String.fromCharCode(...new Uint8Array(signed2))))
return new Response(null, {
status: 204,
headers: {
'Cache-Control': 'no-cache'
}
})
}
This will generate a hash of:
e52613e6ecebdf98bb085f04ca1f91bf9a5cf1dc085f89dcaa3e5fbf5ebf1b06
I've tried use the crypto.subtle.verify method, but that didn't work.
Can anyone see any issues with the code? Or have done this successfully using Cloudflare Workers?
Mark
I finally got this working using the verify method (I had previously tried the verify method, but it didn't work). The main problem seems to the use of request.json() wrapped in JSON.stringify. Changing this to request.text() resolved the issue. I can then use JSON.parse to access the data after verifying the signature. The code is as follows:
const encoder = new TextEncoder()
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const signature = request.headers.get('CloudConvert-Signature')
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(CCSigningKey2),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
)
const data = await request.text()
const verified = await crypto.subtle.verify(
'HMAC',
key,
hexStringToArrayBuffer(signature),
encoder.encode(data)
)
if (!verified) {
return new Response('Verification failed', {
status: 401,
headers: {
'Cache-Control': 'no-cache'
}
})
}
return new Response(null, {
status: 204,
headers: {
'Cache-Control': 'no-cache'
}
})
}
function hexStringToArrayBuffer(hexString) {
hexString = hexString.replace(/^0x/, '')
if (hexString.length % 2 != 0) {
return
}
if (hexString.match(/[G-Z\s]/i)) {
return
}
return new Uint8Array(
hexString.match(/[\dA-F]{2}/gi).map(function(s) {
return parseInt(s, 16)
})
).buffer
}
I have an Axios request to download the .xls file. Problem is that the object returned as a response from backend doesn't always has to be a real file. If I return json object instead of file data. How I would read this json then?
Here is the function:
downloadReport() {
let postConfig = {
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
responseType: 'blob',
} as AxiosRequestConfig
axios
.post(this.urls.exportDiscountReport, this.discount.id, postConfig)
.then((response) => {
let blob = new Blob([response.data], { type: 'application/vnd.ms-excel' });
let url = window['URL'].createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = this.discount.id + ' discount draft.xlsx';
a.click();
window['URL'].revokeObjectURL(url);
})
.catch(error => {
})
}
I would like to read the response and if it contains some data in it - don't create the blob and initiate the download, instead, just show some message or whatever. If I remove the responseType: 'blob' then the .xls file downloads as unreadable and not valid file.
So the problem is that every returned response becomes blob type and I don't see my returned data in it. Any ideas?
I solved this by reading the blob response and checking if it has JSON parameter status. But this looks like an overengineering to me. Is there a better solution?
let postConfig = {
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
responseType: 'blob',
} as AxiosRequestConfig
axios
.post(this.urls.exportDiscountReport, this.discount.id, postConfig)
.then((responseBlob) => {
const self = this;
let reader = new FileReader();
reader.onload = function() {
let response = { status: true, message: '' };
try {
response = JSON.parse(<string>reader.result);
} catch (e) {}
if (response.status) {
let blob = new Blob([responseBlob.data], { type: 'application/vnd.ms-excel' });
let url = window['URL'].createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = self.discount.id + ' discount draft.xlsx';
a.click();
window['URL'].revokeObjectURL(url);
} else {
alert(response.message)
}
}
reader.readAsText(responseBlob.data);
})
.catch(error => {
})
I also found the same solution here: https://github.com/axios/axios/issues/815#issuecomment-340972365
Still looks way too hacky.
Have you tried checking the responseBlob.type property? It gives the MIME type of the returned data.
So for example you could have something like this:
const jsonMimeType = 'application/json';
const dataType = responseBlob.type;
// The instanceof Blob check here is redundant, however it's worth making sure
const isBlob = responseBlob instanceof Blob && dataType !== jsonMimeType;
if (isBlob) {
// do your blob download handling
} else {
responseBlob.text().then(text => {
const res = JSON.parse(text);
// do your JSON handling
});
}
This I find is much simpler and works for me, however it depends on your backend setup. The BLOB response is still a text response, but it's handled slightly differently.
I have a little chat bot for twitch and I want to make now a function where you can change the stream title.
My function looks at the moment so:
public changeTitle = (new_title: string, callback: any): void => {
let t = this;
let request = require('request');
request.get("https://api.twitch.tv/kraken/channels/" + this.settings.current_channel + "?client_id="+this.settings.clientId, { json: true }, function (err, res) {
if (!err && res.statusCode === 200) {
let current_title = res.body.status;
console.log(current_title);
let request2 = require('request');
let options = {
url: "https://api.twitch.tv/kraken/channels/"+t.settings.current_channel+"?channel[status]="+current_title+new_title,
headers: {
"Authorization": "OAuth "+t.settings.clientId,
"Accept": "application/vnd.twitchtv.v2+json"
}
};
request2.put(options, (error: Error, response: any, body: any): void => {
console.log("Title changed?");
});
}
});
};
in the console.log(current_title) I can see the current stream title.
After the console.log("Title changed?") nothing happend.
I get the error 410 GONE. So my way to change the title is no longer supported.
Can someone show me how to change the title?
Thanks in advance :)
This looks to be covered by Update Channel
Specifically, rather than channel[status] use just status as a query param.
let options = {
url: "https://api.twitch.tv/kraken/channels/"+t.settings.current_channel+"?status="+new_title,
headers: {
"Authorization": "OAuth "+t.settings.clientId,
"Accept": "application/vnd.twitchtv.v2+json"
}
You'll also need the channel_editor scope in order to use this on a given channel.
I am trying to display a PDF that is generated from a server onto a view in my Angular 2 RC 5 project. Right now, the ASPNETCORE server is returning the object as an 'application/pdf' and the Angular client is trying to parse the response as a blob. However, I get the following error on the client side:
Error: The request body isn't either a blob or an array buffer
The code that I'm using to call the PDF server is essentially:
getHeaders() : Headers {
var headers = new Headers({
'responseType': 'application/blob'
});
return headers;
}
getBlob() {
return this.http.get(uri, new RequestOptions({headers: this.getHeaders()}, body: "" }))
.map(response => (<Response>response).blob());
}
Try to set the responseType to Blob, it should work:
getBlob() {
return this.http.get(uri, {responseType: ResponseContentType.Blob})
.map(response => (<Response>response).blob());
}
Work's for me :
Component :
downloadInvoice(invoice) {
this.loading = true;
this.invoiceDataService
.downloadInvoice(invoice)
.subscribe(
(blob) => {
FileSaver.saveAs(blob, 'test.pdf');
},
error => this.error = error,
() => {
this.loading = false;
console.log('downloadInvoices : Request Complete')
}
)
}
Data service :
downloadInvoice(invoice): Observable<Blob> {
return this.api.downloadInvoice(invoice);
}
Api service :
downloadInvoice(invoice: Invoice): Observable<Blob> {
return this.authHttp
.get(this.apiBase + '/invoices/' + invoice.hashid + '/download', {responseType: ResponseContentType.Blob})
.map(response => {
return new Blob([response.blob()], {type: 'application/pdf'});
})
.catch(this.handleError.bind(this));
}
Enjoy :)
For Angular 5, ResponseContentType has been deprecated, so a current solution I found was to use:
getFile(): Observable<File> {
let options = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
responseType: 'blob' as 'json'
};
return this.http.get<File>(uri, options);
}