How to send Javascript Object as readable JSON in Slack API? - node.js

I am trying to send a formatted JS object on Slack using the API.
Best I could do was sending to chat.postMessage as:
const testObj = {a: "Hello", b: "World"};
...
data: {
channel: "#my-channel"
text: JSON.stringify(testObj, null, "\t")
}
But it's still not formatted well and can't be minimized like JSON file on Slack.
How can I send it better formatted or as a JSON file?

I would recommend you to use the Text Snippet feature via files.upload method, so you can format the content type properly, in this case javascript.
Send Slack Text Snippet                                                                                
Run in Fusebit
const testObj = {a: "Hello", b: "World"};
const result = await slackClient.files.upload({
channels: slackUserId,
content: testObj,
title: 'Sample',
filetype: 'javascript'
});
const {name, pretty_type} = result.file;
ctx.body = { message: `Successfully sent a text snippet of type ${pretty_type} called ${name} to Slack user ${slackUserId}!` };

I found a good working solution using only native node libraries to get nicely formatted JSON file.
Here's the full code, replace contentObj with whatever object you want to convert to JSON.
Put your designed channel and bot auth token
const https = require('https')
const contentObj = {a: "hello", b: "world"}
const formObject = {
channels: "#myChannel",
content: JSON.stringify(contentObj, null, 2),
filename: "myFile.json",
initial_comment:
"Hello World from JSON HTTPS!",
title: "myFile.json",
};
const urlencodedForm = new URLSearchParams(
Object.entries(formObject)
).toString();
const options = {
hostname: "slack.com",
port: 443,
path: "/api/files.upload",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": urlencodedForm.length,
Authorization: "Bearer " + "Bot Token",
},
};
const req = https.request(options, function (res) {
let responseBody = "";
res.on("data", function (chunk) {
responseBody += chunk;
});
res.on("end", () => {
const finalResponse = JSON.parse(responseBody);
if (finalResponse.ok) {
console.log("successfully sent msg to slack");
} else {
console.log("Message Failed");
}
});
});
req.on("error", (error) => {
console.error(error);
});
req.write(urlencodedForm);
req.end();

Related

nodejs modio api "add modfile" failing to upload

Im trying to upload modfiles with the api but it keeps saying that Im not including filedata. Ive tried with fetch like in the docs but it just gives the same error. If I try http it just gives a list of the files as if it was a GET request.
var zip = `./mod.zip`; // it exists!
var body = {
//filedata: `#${zip}`,
filedata: fs.readFileSync(zip, `binary`),
//filehash: crypto.createHash('md5').update(fs.readFileSync(zip, `binary`)).digest('hex'),
//version: version,
//active: active,
//changelog: changelog,
//metadata_blob: meta,
};
var res = await fetch(`https://api.mod.io/v1/games/${config.modio.gameid}/mods/${config.modio.modid}/files`, { // actually HTTP
method: 'POST',
headers: {
'Authorization': `Bearer ${config.modio.token}`,
'Content-Type': 'multipart/form-data',
'Accept': 'application/json',
},
body: JSON.stringify(body),
});
//console.log(body.filedata);
res = await res.json();
if (res.error)
console.log(res.error);
else
console.log(res);
{
"error": {
"code": 422,
"error_ref": 13009,
"message": "Validation Failed. Please see below to fix invalid input:",
"errors": {
"filedata": "The filedata field is required when upload id is not present.",
"upload_id": "The upload id field is required when filedata is not present."
}
}
}
Yes, ive submitted a bug report to them already, twice. Now they are ghosting me. (I'll probably submit a link to this as well)
They replied!
Hi
As mentioned, you would need to send data using FormData, not JSON.
You can find details at https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
Thanks,
Danny King
Developer Relations Specialist
So, I remade my code
const https = require(`https`);
var crypto = require('crypto');
var FormData = require('form-data');
function ObjectToForm(obj = {}) {
var form = new FormData();
Object.keys(obj).forEach(key => {
var val = obj[key];
switch (typeof val) {
case `boolean`:
val = String(val);
break;
}
form.append(key, val);
});
return form;
}
if (fs.statSync(zip).size > 5368709120) return consolelog(`Zip bigger then 5gb`);
var body = {
filedata: fs.createReadStream(zip),
filehash: crypto.createHash('md5').update(fs.readFileSync(zip)).digest('hex'),
version: version,
active: active,
changelog: changelog,
metadata_blob: meta,
};
var form = ObjectToForm(body);
var options = {
hostname: 'api.mod.io',
port: 443,
path: `/v1/games/${config.modio.gameid}/mods/${config.modio.modid}/files`,
method: 'POST',
headers: {
'Authorization': `Bearer ${config.modio.token}`,
...form.getHeaders(),
'Accept': 'application/json',
},
};
var req = https.request(options, (res) => {
var data = [];
res.on('data', (d) => data.push(d));
req.on(`close`, () => {
var buffer = Buffer.concat(data);
var resp = JSON.parse(buffer.toString());
if (resp.error)
console.log(resp.error);
else if (res.statusCode == 201)
r(true);
});
});
req.on('error', (e) => {
console.log(`Error publishing:`);
console.log(e);
r(e);
});
form.pipe(req)
.on(`close`, () => req.end());
And it worked. Thanks King.

Uploading blob/file in react-native, contents is empty

I am able to succesfully upload a blob with proper contents from my web browser, but when I do it from react-native, the upload file is empty. Here is the code:
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
new File(['foo'], 'foo.txt', {type: 'text/plain'}),
);
await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
}
However doing this same code from react-native, it uploads, but the file is empty.
Here is the node.js server I am using to test this. Loading http://localhost:3002 gives you a button called "upload it". Clicking it does the upload from the web. Screenshots of results are below.
var multiparty = require('multiparty');
var http = require('http');
http
.createServer(function (req, res) {
if (req.url === '/upload' && req.method === 'POST') {
console.log('multipart here');
var form = new multiparty.Form();
form.parse(req, function (err, fields, files) {
console.log(require('util').inspect({ fields, files }, false, null, true));
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ bar: true }));
});
return;
}
console.log('here');
// show a file upload form
res.writeHead(200, { 'content-type': 'text/html' });
res.end(
`
<script>
async function doit() {
const data = new FormData();
data.append('str', 'strvalue');
data.append(
'f',
// new File([new Blob(['asdf'], {type : 'text/plain'})], 'filename.txt'),
new File(['foo', 'what', 'the', 'hell'], 'foo.txt', {type: 'text/plain'}),
);
const res = await fetch('http://localhost:3002/upload', {
method: 'POST',
body: data
});
console.log(JSON.stringify(res, null, 4));
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('b').addEventListener('click', doit, false)
}, false);
</script>
<button type="button" id="b">upload it</button>
`
);
})
.listen(3002);
From web browser we see the node server logs this, notice file size is 14.
However from react-native we see file size is 0:
I faced the same problem recently while posting an image from a react-native app to a server. However, I was able to make it work by appending the name and type of the file to the formData instance.
Here, the uri argument to uploadImageAsync is passed as a route parameter from the previous screen.
const postShoutHandler = async () => {
setShoutUploadStatus("Started Upload");
const response = await uploadImageAsync(route.params.captures);
const uploadResult = await response.json();
if (uploadResult === "Upload successful") {
setShoutUploadStatus("Success");
navigation.navigate("Home");
} else {
setShoutUploadStatus("Failed");
}
};
/* <--Upload image function --> */
const uploadImageAsync = (uri: string) => {
const apiUrl = "https://www.yourserver.com/image";
let uriParts = uri.split(".");
let fileType = uriParts[uriParts.length - 1];
let formData = new FormData();
formData.append("img", {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});
formData.append("description", "HEY");
let options = {
method: "POST",
body: formData,
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + accessToken,
},
};
return fetch(apiUrl, options);
};
/* <--Upload image function --> */
Here is the Image configuration.
const photoData = await camera.takePictureAsync({
base64: true,
exif: false,
});

How do i pass incoming data(NDJ) from an API to another API in CSV format without creating a file?

I have an API to which I am getting a response in NDJ format as shown below and want to pass the response as a csv(comma separated values) to another API
{"country":"Germany","os":"Windows 7","utm_campaign":"(none)","utm_medium":"direct / organic","from_mobile":false,"base_browser":"Chrome","utm_term":"(none)","domain":"furnitureshop4u-2.myshopify.com","browser":"Chrome 76.0.3809","repeat":true,"location":"Meinerzhagen - North Rhine-Westphalia, Germany","base_os":"Windows","utm_source":"(none)"}{"country":"Germany","os":"Windows 7","utm_campaign":"(none)","utm_medium":"direct / organic","from_mobile":false,"base_browser":"Chrome","utm_term":"(none)","domain":"furnitureshop4u-2.myshopify.com","browser":"Chrome 76.0.3809","repeat":true,"location":"Meinerzhagen - North Rhine-Westphalia, Germany","base_os":"Windows","utm_source":"(none)"}
I tried to buffer the response from the source API and changed it using memorystream and converted to csv(comma separated values). Now I am struck here I dont know how to transfer the data to the destination API.
var body = {"request_json" :JSON.stringify(options)};
var apicall = {
url: API,
method: 'post',
responseType: 'stream',
headers: {
'Content-Type': 'application/json',
},
data: body
}
axios(apicall).then((res) =>{
var writer = new MemoryStream(null, {
readable : true
}) // using memorystream to stream the buffer
res.data.pipe(writer); // writing data to a writer
writer.on('data',function(dat){
console.log(dat); //getting buffer
var e = dat.toString();
console.log(e);
var x = JSON.parse(e);
var y = [x];
console.log(y); // converted to JSON
var keys = Object.keys(x);
var csv = json2csv.convertArrayOfObjects(y, keys) //converting to csv comma separated string
console.log(csv,'Final');
});
writer.on('end', function(){
//console.log('updated ---- ',data);
});
}).catch((e)=>{
console.log(e);
})
I want to just pipe the data from source API to destination API in a CSV format without storing the data into a csv file.
As we are already converting incoming data to stream using memorystream then we can convert it and stream directly to destination API as below
const axios = require('axios')
const FormData = require('form-data')
const MemoryStream = require('memorystream')
const { AsyncParser } = require('json2csv')
const body = { request_json: JSON.stringify(options) }
const apicall = {
url: API,
method: 'post',
responseType: 'stream',
headers: {
'Content-Type': 'application/json'
},
data: body
}
axios(apicall)
.then(res => {
var writer = new MemoryStream(null, {
readable: true
})
const transformOpts = { encoding: 'utf-8' }
const asyncParser = new AsyncParser(transformOpts)
asyncParser.fromInput(res.data).toOutput(writer)
const formData = new FormData()
formData.append('source', 'sample_table')
formData.append('file', writer, {filename:file,contentType:'text/csv'})
axios
.post(finalAPI, formData, {
headers: formData.getHeaders(),
maxContentLength: 2 * 1024 * 1024 * 1024
})
.then(function(a) {
console.log('SUCCESS!!', a)
})
.catch(function(e) {
console.log('FAILURE!!', e)
})
})
.catch(e => {
console.log(e)
})

aws elastic search http request , error 'The bulk request must be terminated by a newline'

I have used this link to create a bulk http request using a JSON.stringified(body) like this:
const body = [ { index: { _index: 'image_2', _type: '_doc', _id: 0 } },
{ imageKey: 'test' },
{ index: { _index: 'image_2', _type: '_doc', _id: 1 } },
{ imageKey: 'test2' } ]
but I keep getting the error
{ statusCode: 400,
body: '{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\\\n]"}],"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\\\n]"},"status":400}' }
I already tried this below and it is not working either:
const body = `${JSON.stringified(body)\n}`;
any ideas are appreciated :)
here is my aws elastic search function:
function elasticsearchFetch(elasticsearchDomain, endpointPath, options = {}, region = process.env.AWS_REGION) {
return new Promise((resolve, reject) => {
const { body, method = 'GET' } = options;
const endpoint = new AWS.Endpoint(elasticsearchDomain);
const request = new AWS.HttpRequest(endpoint, region);
request.method = method;
request.path += endpointPath;
request.headers.host = elasticsearchDomain;
if (body) {
request.body = body;
request.headers['Content-Type'] = 'application/json';
request.headers['Content-Length'] = request.body.length;
}
const credentials = new AWS.EnvironmentCredentials('AWS');
const signer = new AWS.Signers.V4(request, 'es');
signer.addAuthorization(credentials, new Date());
const client = new AWS.HttpClient();
client.handleRequest(request, null, (res) => {
res.on('data', (chunk) => {
chunks += chunk;
});
res.on('end', () => resolve({ statusCode: res.statusCode, body: chunks }));
}, error => reject(error));
});
}
and here is the code for my lambda function using the above request signer:
const elasticsearchFetch = require('rms-library/fetch');
exports.handler = async ({ imageID, bulk, products }) => {
if (!Array.isArray(products) || !imageID) {
throw new Error('error in bulk operation');
}
const bulkList = [];
products.forEach((product, i) => {
bulkList.push({ index: { _index: imageID, _type: '_doc', _id: i } });
bulkList.push(product);
});
bulkList.push('');
console.log('bulkList', bulkList);
const result = await elasticsearchFetch.elasticsearch(
process.env.ELASTIC_SEARCH_DOMAIN,
'_bulk',
{ method: 'PUT', body: JSON.stringify(bulkList) },
);
console.log(result);
};
Ok, first of all you need to use POST instead of PUT with the _bulk endpoint and the body cannot be a stringified JSON array.
Try like this instead:
const result = await elasticsearchFetch.elasticsearch(
process.env.ELASTIC_SEARCH_DOMAIN,
'_bulk',
{ method: 'POST', body: bulkList.map(json => {return JSON.stringify(json);}).join('\n') + '\n' },
);

One Signal Push Notification

How to include small icon and big icon url while creating a push notification in node js, I know that we need to send image url while sending push notification, i need code sample in node js.. I have this code..
sendNotificationToSpecific =function (token,messages) {
var sendNotification = function (data) {
var headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic MDNlMTNjYWMTgy"
};
var options = {
host: "onesignal.com",
port: 443,
path: "/api/v1/notifications",
method: "POST",
headers: headers
};
var https = require('https');
var req = https.request(options, function (res) {
res.on('data', function (data) {
console.log("Response:");
console.log(JSON.parse(data));
});
});
req.on('error', function (e) {
console.log("ERROR:");
console.log(e);
});
req.write(JSON.stringify(data));
req.end();
};
var message = {
app_id: "awer342-d744-4787-b59a-f55c6215c491",
contents: {"en": messages},
include_player_ids: [token],
};
sendNotification(message);
};
As you can see in the docs https://documentation.onesignal.com/reference#section-appearance you can simply extend your message object like
var message = {
app_id: "xxxx",
contents: {"en": messages},
include_player_ids: [token],
small_icon: "resource_name", // can not be an url
large_icon: "http://url/ or resource_name"
}

Resources