Using slack webhook with node - slack

I am trying to use slack webhook. I can read a lot of variation about how I should proceed, but until now, none of them worked properly.
I am using the request node module to make the api call, but I can change if needed.
First try following this
import request from 'request';
const url = 'https://hooks.slack.com/services/xxx';
const text = '(test)!';
request.post(
{
headers : { 'Content-type' : 'application/json' },
url,
payload : JSON.stringify({ text }),
},
(error, res, body) => console.log(error, body, res.statusCode)
);
I get : null 400 'invalid_payload'
Next try following this
request.post(
{
headers : { 'Content-type' : 'application/json' },
url,
form : JSON.stringify({ text }),
},
(error, res, body) => console.log(error, body, res.statusCode)
);
This time, it works, but Slack displays: %28test%29%21 instead of (test)!
Did I miss something?

Based on your second example and the working Postman request this is how I got it to work, forgive my change to require as I am running older node version right now. I am not exactly sure what your data would look like that you want to post to Slack, that may change how you want to assemble this.
const request = require('request');
const url = 'https://hooks.slack.com/services/xxxxx';
const text = '(test)!';
request.post(
{
headers : { 'Content-type' : 'application/json' },
url,
form : {payload: JSON.stringify({ text } )}
},
(error, res, body) => console.log(error, body, res.statusCode)
);
If you want to use request you may want to check how slack-node is posting the data, here the relevant snipped from slack-node
Slack.prototype.webhook = function(options, callback) {
var emoji, payload;
emoji = this.detectEmoji(options.icon_emoji);
payload = {
response_type: options.response_type || 'ephemeral',
channel: options.channel,
text: options.text,
username: options.username,
attachments: options.attachments,
link_names: options.link_names || 0
};
payload[emoji.key] = emoji.val;
return request({
method: "POST",
url: this.webhookUrl,
body: JSON.stringify(payload),
timeout: this.timeout,
maxAttempts: this.maxAttempts,
retryDelay: 0
}, function(err, body, response) {
if (err != null) {
return callback(err);
}
return callback(null, {
status: err || response !== "ok" ? "fail" : "ok",
statusCode: body.statusCode,
headers: body.headers,
response: response
});
});
};

You can try the slack-node module, wraps the post to the hook. Here a reduced modified real world example I used to push notifications for AWS instances.
[EDIT] Changed to use your text
Now, using slack-node, you assemble the {} yourself, adding text: and other parameters yourself and pass it to .webhook
const Slack = require('slack-node');
const webhookUri = 'https://hooks.slack.com/services/xxxxx';
const slack = new Slack();
slack.setWebhook(webhookUri);
text = "(test)!"
slack.webhook({
text: text
// text: JSON.stringify({ text })
}, function(err, response) {
console.log(err, response);
});

I finally went with Slack-webhook that I liked better than slack-node. The solution of d parolin is the best answer to my question, but I wanted to mention the work of pthm for completion.

Related

Posting image from expo and axios to spring boot server returns error

I wanna send an axios request with photo data to my Spring Boot server but it does not work.
Here is the code:
const updateUserProfile = dispatch => async ({categories, phoneNumber, photo}) => {
try {
const id = await SecureStore.getItemAsync('user_id');
const formData = new FormData()
formData.append("file", {
uri: photo,
name: `${id}_photo`,
type: 'image/png'
})
await request.post(
`/photos/${id}`,
formData,
{
headers: {
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`
},
},
)
dispatch({type: 'update_user_profile', payload: response.data})
} catch (e) {
dispatch({type: 'add_error', payload: 'UPDATE_USER_PROFILE_ERROR'})
}
}
The file URI looks like that and I think its correct:
file:///data/user/0/[...]/ImagePicker/e9255306-dca9-486e-a9
05-e4e1c619b766.jpg
And here is the Spring Boot Controller
#PostMapping("/{userId}")
public void saveObject(#RequestParam(value = "file") MultipartFile file, #PathVariable Long userId) {
photoService.uploadFile(file, userId);
}
Spring Boot works great when I send request with photo from postman but when I want to send the request from updateUserProfile method above, I receive this error:
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
Okay, finally after many hours I fixed the problem. This github issue helped me a lot https://github.com/axios/axios/issues/4412
So I installed form-data package
npm i form-data
and in the updateUserProfile method added this line just before appending data to formData
FormData.prototype[Symbol.toStringTag] = 'FormData';
so the full method looks like that now:
const updateUserProfile = dispatch => async ({categories, phoneNumber, photo}) => {
try {
const id = await SecureStore.getItemAsync('user_id');
const extenstion = photo.substring(photo.lastIndexOf('.') + 1)
const fileName = photo.replace(/^.*[\\\/]/, '')
const formData = new FormData()
FormData.prototype[Symbol.toStringTag] = 'FormData';
formData.append("file", {
uri: photo,
name: fileName,
type: `image/${extenstion}`
})
await request.post(
`/photos/${id}`,
formData,
{
headers: {
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`
},
},
)
dispatch({type: 'update_user_profile', payload: response.data})
} catch (e) {
dispatch({type: 'add_error', payload: 'UPDATE_USER_PROFILE_ERROR'})
}
}

Cypress: How do I pass a selected property from API response to another API request?

I would like to use Cypress for API testing. My goal is to extract a part of the API response and pass it to another API request. Here's a sample code:
Cypress.Commands.add('createCustomer', () => {
return cy.request({
method: 'POST',
url: 'api/v1/Customers',
headers: {
'Content-Type': 'application/json'
},
body: {
// sample content
}
}).then((response) => {
return new Promise(resolve => {
expect(response).property('status').to.equal(201)
expect(response.body).property('id').to.not.be.oneOf([null, ""])
const jsonData = response.body;
const memberId = jsonData.id
resolve(memberId)
return memberId
})
})
})
With this code, I am getting [object%20Object] as the result.
Hoping for some feedback.
So you are adding the id generated by the POST to a subsequent GET request?
Try returning the id without using a Promise, I don't think you need one at that point since the response has already arrived.
}).then((response) => {
expect(response).property('status').to.equal(201)
expect(response.body).property('id').to.not.be.oneOf([null, ""])
const jsonData = response.body;
const memberId = jsonData.id;
return memberId;
})
Url for GET
cy.createCustomer().then(id => {
const url = `api/v1/Customers${id}`;
...
or
cy.createCustomer().then($id => {
const id = $id[0]; // Not quite sure of the format, you may need to "unwrap" it
const url = `api/v1/Customers${id}`;
...
If you want to pass response from API Request 1 to API Request 2, you can do something like this:
describe('Example to demonstrate API Chaining in Cypress', function () {
it('Chain two API requests and validate the response', () => {
//Part 1
cy.request({
method: 'GET',
url: 'https://www.metaweather.com/api/location/search/?query=sn',
}).then((response) => {
const location = response.body[0].title
return location
})
//Part 2
.then((location) => {
cy.request({
method: 'GET',
url: 'https://www.metaweather.com/api/location/search/?query=' + location
}).then((response) => {
expect(response.status).to.eq(200)
expect(response.body[0]).to.have.property('title', location)
})
})
})
})
Your code seems to be failing during the initial request, not during the subsequent actions. I am far from a Javascript expert, but you seem to have some unnecessary complexity in there. Try simplifying your command like this and see if you can at least get a successful request to go through:
Cypress.Commands.add('createCustomer', () => {
cy.request({
method: 'POST',
url: 'api/v1/Customers',
headers: {
'Content-Type': 'application/json'
},
body: {
// sample content
}
})
})
And if that works, keep going:
Cypress.Commands.add('createCustomer', () => {
cy.request({
method: 'POST',
url: 'api/v1/Customers',
headers: {
'Content-Type': 'application/json'
},
body: {
// sample content
}
}).then((response) => {
expect(response).property('status').to.equal(201)
expect(response.body).property('id').to.not.be.oneOf([null, ""])
const jsonData = response.body;
const memberId = jsonData.id
return memberId
})
})

Redux says to use Plain object in action.But dont know where to use plain object

I was searching for my answer in SO, but could not find any suitable one. So here i go with my questions...
In my redux action creator i am fetching API call from isomorphic-unfetch but I am getting the message Error: Actions must be plain objects. Use custom middleware for async actions each time.Though i defined dispatch in my action...
My action code is
const exchangeBuy = ({btc, usdt, id}, url) => {
return (dispatch) => {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( { btc, usdt, id } )
}).then(
r => r.json()
).then(
r => dispatch({
type: 'EXCHANGE_BUY',
payload: r //here r return an object from mongoDB
})
)
}
}
Also the code that invokes this is
submitExchange(e){
e.preventDefault()
const btc = e.target.btcamount.value
const usdt = e.target.usdprice.value
this.props.exchangeBuy( //Here it is
{
btc: btc,
usdt: usdt,
id: this.props.users.id
},
this.props.apiurl )
}

React-native : response of fetch impossible to treat

Still learning on RN... I'm trying to use fetch() in react-native to get a specific data from my server, before opening a webpage in smartphone's browser.
Here is what I wrote :
openLink = () => { //Communicate to the server to get an unique key_id
this.state = {urlKey: 'text'}; //Initial state
var params = {
// Some params send by POST to authenticate the request...
};
var formData = new FormData();
for (var k in params) {
formData.append(k, params[k]);
}
fetch(Constants.URL.root+"mobile/authorize_view", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
},
body: formData
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({urlKey:responseJson.document_key}); //Getting the response, and changing the initial state (was 'text' previously)
})
.done();
var urlString = Constants.URL.upload + '/' + this.state.urlKey; // !!Problem : opening in browser with this.state.urlKey = text, and not document_key!!
Linking.canOpenURL(urlString).then(supported => {
if (supported) {
Linking.openURL(urlString);
} else {
console.log('Don\'t know how to open URI: ' + this.props.url);
}
});
}
Actually, as you can see, I ask for a specific key to my server (urlKey, that is returned in a JSON Object : responseJson.document_key).
Everything is running well in server's part, cause I put this generated document_key in my Database, and I can see it is put correctly.
The problem is in React-native part : the browser opens a webpage with this.state.urlKey as **text** which is the initial state that the function fetch should have turned into the document_key sent by server...
What am I missing ?
The fetch statement is asynchronous. Meaning when you call fetch then next line of execution not necessary the .then but is
var urlString = Constants.URL.upload + '/' + this.state.urlKey;
Note by this stage if .then isnt complete fetching the data your this.state.document_key will not be populated. Hence why you see the error
Instead move that code in the final then e.g:
openLink = () => { //Communicate to the server to get an unique key_id
this.state = {urlKey: 'text'}; //Initial state
var params = {
// Some params send by POST to authenticate the request...
};
var formData = new FormData();
for (var k in params) {
formData.append(k, params[k]);
}
fetch(Constants.URL.root+"mobile/authorize_view", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
},
body: formData
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({urlKey:responseJson.document_key}); //Getting the response, and changing the initial state (was 'text' previously)
//moved inside then
var urlString = Constants.URL.upload + '/' + this.state.urlKey; // !!Problem : opening in browser with this.state.urlKey = text, and not document_key!!
Linking.canOpenURL(urlString).then(supported => {
if (supported) {
Linking.openURL(urlString);
} else {
console.log('Don\'t know how to open URI: ' + this.props.url);
}
});
})
.done();
}

Slack WebAPI fails with not_authed

I'm attempting to post interactive messages to slack as a Bot User (using chat.postMessage, etc).
Although I am passing in the Bot Access Token (as received from the initial OAuth) I keep getting an error response stating "not_authed".
I get the same when I attempt auth.test.
I'm doing something like the following with "request" in node.js:
app.get("/testAuth/test", function(req,res){
console.log("in testAuth/test...sending test message to Slack");
var bToken = process.env.TESTBOT_ACCESS_TOKEN;
var slackMessageURL = "https://slack.com/api/auth.test";
var postOptions = {
uri: slackMessageURL,
method: "POST",
token: bToken
};
request(postOptions, (error, response, body) => {
if(error){
console.log("OOPPPPS....we hit an error in auth.test: " + error);
} else {
console.log("auth.test response: " + JSON.stringify(response));
}
});
res.send("Sent Test...check logs");
});
which results with:
auth.test response: {"statusCode":200,"body":"{\"ok\":false,\"error\":\"not_authed\"}",...
According to the Slack WebAPI docs, if I'm posting as the Bot, I should use the Bot's access token (as received from the initial oauth), but figure I'm either formatting my request incorrectly, or the token is not what Slack is expecting.
Ok, after talking with Slack support, it appears (at least) the WebAPIs I am calling don't yet support application/json. These do work with x-www-form-urlencoded.
Looking at this post
I was able to cobble together the following which auth'd successfully:
//up top
var request = require("request");
var querystring = require("querystring");
//...
app.get("/testAuth/test", function(req,res){
console.log("in testAuth/test...sending test message to Slack");
var bToken = process.env.TESTBOT_ACCESS_TOKEN;
var message = {
token: bToken
};
var messageString = querystring.stringify(message);
var messageLength = messageString.length;
var slackMessageURL = "https://slack.com/api/auth.test";
var postOptions = {
headers: {
"Content-length": messageLength,
"Content-type": "application/x-www-form-urlencoded"
},
uri: slackMessageURL,
body: messageString,
method: "POST"
};
request(postOptions, (error, response, body) => {
if(error){
console.log("OOPPPPS....we hit an error in auth.test: " + error);
} else {
console.log("auth.test response: " + JSON.stringify(response));
}
});
res.send("Sent Test...check logs");
});

Resources