How to send query string parameters using supertest? - supertest

I'm using supertest to send get query string parameters, how can I do that?
I tried
var imsServer = supertest.agent("https://example.com");
imsServer.get("/")
.send({
username: username,
password: password,
client_id: 'Test1',
scope: 'openid,TestID',
response_type: 'token',
redirect_uri: 'https://example.com/test.jsp'
})
.expect(200)
.end(function (err, res) {
// HTTP status should be 200
expect(res.status).to.be.equal(200);
body = res.body;
userId = body.userId;
accessToken = body.access_token;
done();
});
but that didn't send the parameters username, password, client_id as query string to the endpoint. Is there a way to send query string parameters using supertest?

Although supertest is not that well documented, you can have a look into the tests/supertest.js.
There you have a test suite only for the query strings.
Something like:
request(app)
.get('/')
.query({ val: 'Test1' })
.expect(200, function(err, res) {
res.text.should.be.equal('Test1');
done();
});
Therefore:
.query({
key1: value1,
...
keyN: valueN
})
should work.

it is not the best way but you also can do this:
await supertest(BASE_URL)
.get(`/?search=${Title}&`)

Related

Is it possible to return or await for return of cypress intercept?

I would like to get the response of an intercept cypress call while running an e2e test:
const registerUser = async (username, password)=>{
cy.visit('<user_registration_url');
cy.intercept('/registration').as('registration');
cy.get('input#username').type(username);
cy.get('input#password').type(password);
cy.get('button#submit').click()
// Expected response:
//{statusCode: 200, body: {userId: <string>}}
const result = await cy.wait('#registration');
const userId = result.response.body.userId;
return userId
it('should test something with a newly registered user', ()=>{
const userId = registerUser('foobar', 'password');
// ... keep testing using the userId which was generated on the backend.
});
No, this won't work.
Cypress is asynchronous, so the commands won't return any data that you can assign to variables. You can read more on then in Cypress docs.
You can get to the response like so:
cy
.wait('#registration')
.then(response => {
// some other code
});
or you can check e.g. a status code like so:
cy
.wait('#registration')
.its('response.statusCode')
.should('eq', 201);
You can see many examples on the page for cy.intercept command.
So indeed as #pavelsman mentioned there is no async/await syntax for cypress. In order to make utility functions one can return a chainable as in this example:
const registerUser = (username, password)=>{
cy.visit('<user_registration_url');
cy.intercept('/registration').as('registration');
cy.get('input#username').type(username);
cy.get('input#password').type(password);
cy.get('button#submit').click()
// Expected response:
//{statusCode: 200, body: {userId: <string>}}
return cy.wait('#registration');
it('should test something with a newly registered user', ()=>{
registerUser('foobar', 'password').then(result=>{
const userId = result.response.body.userId;
// ... keep testing using the userId which was generated on the backend.
});
});

How to get an access_token from googleapis?

I am trying to get an access token for accessing the Firebase Hosting API from a Service account, as described here.
The code below does not return an access_token, but an id_token instead, which fails to authenticate when trying to use the API.
What am I doing wrong? How can I obtain an access token?
const { google } = require("googleapis");
var serviceAccount = require("../functions/src/services/serviceAccountKey.json");
async function getAccessToken() {
try {
const jwtClient = new google.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
["firebasehosting.googleapis.com"],
null
);
const credentials = await jwtClient.authorize();
console.log(credentials);
} catch (error) {
console.log(error);
}
}
getAccessToken();
It returns a credentials object:
{
access_token: undefined,
token_type: 'Bearer',
expiry_date: undefined,
id_token: '...', // edited out
refresh_token: 'jwt-placeholder'
}
For the record, I finally got it.
My token scope was invalid: I should use https://www.googleapis.com/auth/firebase
The valid scopes are listed here

Authorize GMail API with JWT

I'm trying to send an email through the gmail API from a Node.js application. I had this working, following the documentation and using the node-mailer package. However, I noticed that when we change our organizations password, the connection is no longer good (which makes sense). I'm therefore trying to authorize with a JWT instead.
The JWT is correctly generated and posted to https://oauth2.googleapis.com/token. This request then returns an access_token.
When it comes time to write and send the email, I tried to simply adapt the code that was previously working (at the time with a client_secret, client_id and redirect_uris):
const gmail = google.gmail({ version: 'v1', auth: access_token });
gmail.users.messages.send(
{
userId: 'email',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
The API keeps returning Error: Login Required.
Does anyone know how to solve this?
EDIT
I've modified my code and autehntication to add the client_id and client_secret:
const oAuth2Client = new google.auth.OAuth2(
credentials.gmail.client_id,
credentials.gmail.client_secret,
credentials.gmail.redirect_uris[0]
);
oAuth2Client.credentials = {
access_token: access_token
};
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
gmail.users.messages.send(
{
userId: 'email',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
But now the error is even less precise: Error: Bad Request
Here's the final authorization code that worked for me:
var credentials = require('../../credentials');
const privKey = credentials.gmail.priv_key.private_key;
var jwtParams = {
iss: credentials.gmail.priv_key.client_email,
scope: 'https://www.googleapis.com/auth/gmail.send',
aud: 'https://oauth2.googleapis.com/token',
exp: Math.floor(new Date().getTime() / 1000 + 120),
iat: Math.floor(new Date().getTime() / 1000),
sub: [INSERT EMAIL THAT WILL BE SENDING (not the service email, the one that has granted delegated access to the service account)]
};
var gmail_token = jwt.sign(jwtParams, privKey, {
algorithm: 'RS256'
});
var params = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: gmail_token
};
var params_string = querystring.stringify(params);
axios({
method: 'post',
url: 'https://oauth2.googleapis.com/token',
data: params_string,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(response => {
let mail = new mailComposer({
to: [ARRAY OF RECIPIENTS],
text: [MESSAGE CONTENT],
subject: subject,
textEncoding: 'base64'
});
mail.compile().build((err, msg) => {
if (err) {
return console.log('Error compiling mail: ' + err);
}
const encodedMessage = Buffer.from(msg)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
sendMail(encodedMessage, response.data.access_token, credentials);
});
});
So that code segment above uses a private key to create a JSON Web Token (JWT), where: iss is the service account to be used, scope is the endpoint of the gmail API being accessed (this must be preauthorized), aud is the google API oAuth2 endpoint, exp is the expiration time, iat is the time created and sub is the email the service account is acting for.
The token is then signed and a POST request is made to the Google oAuth2 endpoint. On success, I use the mailComposer component of NodeMailer to build the email, with an array of recipients, a message, a subject and an encoding. That message is then encoded.
And here's my sendMail() function:
const oAuth2Client = new google.auth.OAuth2(
credentials.gmail.client_id,
credentials.gmail.client_secret,
credentials.gmail.redirect_uris[0]
);
oAuth2Client.credentials = {
access_token: access_token
};
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
gmail.users.messages.send(
{
userId: 'me',
resource: {
raw: encodedMessage
}
},
(err, result) => {
if (err) {
return console.log('NODEMAILER - The API returned: ' + err);
}
console.log(
'NODEMAILER Sending email reply from server: ' + result.data
);
}
);
In this function, I am creating a new googleapis OAuth2 object using the credentials of the service account (here stored in an external file for added security). I then pass in the access_token (generated in the auth script with the JWT). The message is then sent.
Pay attention to the userId: 'me' in the sendMail() function, this was critical for me.
This is the way I was able to only use googleapis package instead of axios + googleapis with your service account. You will need domain wide authority for this account with the scope used below associated with it. Follow this to do that https://support.google.com/a/answer/162106?hl=en
You can also use the mailComposer example up above to create the email. keys is the service_credentials.json file you get when making this service account
const { google } = require('googleapis');
const scope = ["https://www.googleapis.com/auth/gmail.send"];
const client = new google.auth.JWT({
email: keys.client_email,
key: keys.private_key,
scopes: scope,
subject: "emailToSendFrom#something.com",
});
await client.authorize();
const gmail = google.gmail({ version: 'v1', auth: client});
const subject = '🤘 Hello 🤘';
const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
const messageParts = [
'From: Someone <emailToSendFrom#something.com>',//same email as above
'To: Someone <whoever#whoever.com>',
'Content-Type: text/html; charset=utf-8',
'MIME-Version: 1.0',
`Subject: ${utf8Subject}`,
'',
'This is a message just to say hello.',
'So... <b>Hello!</b> 🤘❤️😎',
];
const message = messageParts.join('\n');
// The body needs to be base64url encoded.
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
const res = await gmail.users.messages.send({
userId: 'me',
requestBody: {
raw: encodedMessage,
},
});
console.log(res.data);

How to use rxjs ajax operator instead of axios in my react project?

I am new to rxjs and want to know how to handle this use case.
This is axios promise, how to convert it so that it uses rxjs's ajax operator
export const onLogin = ({ username, password }) =>
axios({
method: "post",
url: O_TOKEN_URL,
data: querystring.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
This is my action,
export const onSubmit = payload => ({
type: FETCH_USER,
payload // payload contains username & password
});
This is my epic for now,
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
// somehow import onLogin from above and resolve it
// then, dispatch FETCH_USER_FULFILLED
.do(
payload => console.log(payload.username, payload.password)
// i am able to console these username and password
)
.mapTo(() => null);
I want to resolve onLogin function somehow, when FETCH_USER is dispatched, using rxjs's ajax operator.
And, I want onLogin function, which returns promise/observable, to be set up in different file so that I can keep track of all the ajax requests
These are the packages,
"redux-observable": "^0.18.0",
"rxjs": "^5.5.10",
Could you also point me to a documentation that covers this and various use case for post, put ... requests? I couldn't find any.
The ajax config object is fairly similar to what you already have. I'm assuming the data property for the axios request is the request body.
import {ajax} from 'rxjs/observable/dom/ajax';
export const onLogin = ({ username, password }) =>
ajax({
method: "POST",
url: O_TOKEN_URL,
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
Your epic would look something like this:
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
.mergeMap(action =>
onLogin(action.payload)
// map will be called when the request succeeded
// so we dispatch the success action here.
.map((ajaxResponse) => fetchUserFulfilled())
// catch will be called if the request failed
// so we dispatch the error action here.
// Note that you must return an observable from this function.
// For more advanced cases, you can also apply retry logic here.
.catch((ajaxError, source$) => Observable.of(fetchUserFailed()))
);
Where fetchUserFulfilled and fetchUserFailed are action creator functions.
There does not seem to be much documentation of the RxJS 5 ajax method yet. Here are the links to the official v5 docs for the AjaxRequest, AjaxResponse and AjaxError. The AjaxError object in particular has 0 information so far (at the time of this answer) so you will need to rely on the source code if you need to use this object for more than a trigger to tell the user that something went wrong. The ajax source code is here.

Difficulty creating a functional Angular2 post

I'm trying to send a post request to another service (a Spring application), an authentication, but I'm having trouble constructing a functional Angular2 post request at all. I'm using this video for reference, which is pretty new, so I assume the information still valid. I'm also able to execute a get request with no problems.
Here's my post request:
export class LogIn {
authUser: string;
authPass: string;
token: any;
constructor(private _http:Http){}
onSubmit() {
var header = new Headers()
var json = JSON.stringify({ user: this.authUser, password: this.authPass })
var params2 = 'user=' + this.authUser + '&password=' + this.authPass
var params = "json=" + json
header.append('Content-Type', 'application/x-www-form-urlencoded')
this._http.post("http://validate.jsontest.com", params, {
headers: header
}).map(res => res.json())
.subscribe(
data => this.token = JSON.stringify(data),
err => console.error(err),
() => console.log('done')
);
console.log(this.token);
}
}
The info is being correctly taken from a form, I tested it a couple of times to make sure. I am also using two different ways to build the json (params and params2). When I try to send the request to http://validate.jsontest.com, the console prints undefined where this.token should be. When I try to send the request to the Spring application, I get an error on that side:
Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
Does anyone know what I'm doing wrong?
In fact you need to use the GET method to do that:
var json = JSON.stringify({
user: this.authUser, password: this.authPass
});
var params = new URLSearchParams();
params.set('json', json);
this._http.get("http://validate.jsontest.com", {
search: params
}).map(res => res.json());
See this plunkr: http://plnkr.co/edit/fAHPp49vFZJ8OuPC1043?p=preview.

Resources