Why does my API code return "You are not subscribed to this API." even with the "X-RapidAPI-Key"? - api

I am trying to execute the an API which I found on rapidapi.com.
The url of the API is https://rapidapi.com/sheharyar566/api/random-words5/?utm_source=ANIA-KUBOW&utm_medium=DevRel&utm_campaign=DevRel.
I copied the code from the site which includes the "X-RapidAPI-Key" value when you are logged in.
But when I execute the code on node, I get the message "You are not subscribed to this API."
How do I get it to return the correct result, which is a randomly generated word?
Below is the code:
import fetch from 'node-fetch';
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "KEY-GOES-HERE",
"X-RapidAPI-Host": "random-words5.p.rapidapi.com",
},
};
fetch(
"https://random-words5.p.rapidapi.com/getMultipleRandom?count=5&wordLength=5",
options
)
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => console.error(err));
And below is the error message:
{ message: 'You are not subscribed to this API.' }

As the error message states, you are not subscribed to the API. RapidAPI supports 3 tiers of subscriptions:
Free
Freemium
Paid
On free APIs, the subscription will be auto-created on the first call. For freemium and paid, you will need to navigate the plans tab and select the right plan for you.
In your case, go to https://rapidapi.com/sheharyar566/api/random-words5/pricing and select one of the plans.

Related

Get a JSON file from an AppScript backend, using an AppScript front end, without getting a CORS error?

I'm trying to build a an API-driven front end in Google AppsScript that calls a REST API hosted on AppScript to make some database queries.
I am currently simply trying to retrieve a JSON file with a GET request.
Everything I try, I get "CORS Missing Allow Origin".
My understand of CORS is that I might experience this with POST request (but maybe there's some people who have phrased their requests to get work this?)
I have a sense that the situation has changed over time, and what has worked in previous SO threads, doesn't seem to work for me now.
Sigh. I feel like Google's Documentation Team would benefit from a dedicated article to explaining how this is supposed to work.
If anyone can shed light on how I can get this to work, I've be most grateful:
client side code:
useEffect(() => {
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
}, []);
Server side code:
export function doGet(e) {
if (e.pathInfo.startsWith('get/all')) {
return getAllRecords(e);
}
else if (e.pathInfo.startsWith('get')) {
return getRecord(e);
}
else {
return getAllRecords(e);
//return HtmlService.createHtmlOutput('Error: invalid path- ' + e.pathInfo + '\n\n' + e.parameter + e);
}
}
function getAllRecords(e) {
// Connect to the MySQL database using the JDBC connector
const conn = Jdbc.getConnection(url, username, password);
// Construct the SELECT statement
const sql = `SELECT * FROM cars LIMIT 100`;
// Execute the INSERT statement
const stmt = conn.prepareStatement(sql);
const results = stmt.executeQuery();
// Return the inserted record with the generated id
const records = [];
while (results.next()) {
const record = {
id: results.getInt('id'),
name: results.getString('name'),
make: results.getString('make'),
price: results.getInt('price')
};
records.push(record);
}
return ContentService.createTextOutput(JSON.stringify(records)).setMimeType(ContentService.MimeType.TEXT);
// return ContentService.createTextOutput(JSON.stringify(records)).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I've tried various combination of MIME Type, and request headers and I'll try any combinations people suggest.
In order to use pathInfo, in this case, it is required to use the access token. I thought that this might be the reason for your current issue. But, when the access token is used, I'm worried that is might not be useful for your actual situation. So, in this answer, I would like to propose the following 2 patterns.
Pattern 1:
In this pattern, your script is modified using the access token. In this case, please modify your Javascript as follows.
From:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
To:
const accessToken = "###"; // Please set your access token.
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all?access_token=' + accessToken)
.then(result => result.json())
.then(rowData => setRowData(rowData))
When you use the access token, please include the scopes of Drive API. Please be careful about this.
Pattern 2:
In this pattern, I would like to propose the modification without using the access token. When the access token cannot be used, unfortunately, pathInfo cannot be used. So, in this pattern, the query parameter is used instead of pathInfo.
Please modify your Javascript as follows.
From:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec/get/all', {
redirect: "follow",
headers: {
"Content-Type": "text/plain",
},
})
.then(result => result.json())
.then(rowData => setRowData(rowData))
To:
fetch('https://script.google.com/macros/s/AKfycbz3_hgjZe0E35ZI2mw7aNs3ASkYCct77qIzL_WTOQMu_ZZeax9WpHpPIwm-MFPhZAW77g/exec?value=get%2Fall') // or ?value=get
.then(result => result.json())
.then(rowData => setRowData(rowData))
And also, please modify doGet of your Google Apps Script as follows.
Modified script:
function doGet(e) {
if (e.parameter.value == "get/all") {
return getAllRecords(e);
} else if (e.parameter.value = "get") {
return getRecord(e);
} else {
return getAllRecords(e);
}
}
Note:
In this modification, it supposes that your getAllRecords(e) works fine. Please be careful about this.
And, in this modification, it supposes that your Web Apps is deployed as Execute as: Me and Who has access to the app: Anyone. Please be careful about this.
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
Thit is a sample modification. So, please modify this for your actual situation.
Reference:
Taking advantage of Web Apps with Google Apps Script (Author: me)

Post Confirmation ConfirmForgotPassword lambda execution returns InvalidLambdaResponseException - Unrecognizable lambda output

I have spent a bunch of hours to solve this issue, all related SO answers seem to be outdated or irrelevant.
State:
Amplify.js "^4.3.10" with code
Auth.forgotPasswordSubmit(email, confirmationCode, password)
.then(_ => this.notification.success("Confirmation OK"))
.catch(err => console.log(err));
Lambda on Node.js 14
Cognito trigger "Post confirmation"
Lambda function code:
'use strict';
exports.handler = async (event, context, callback) => {
if (event.triggerSource === "PostConfirmation_ConfirmForgotPassword") {
const postConfirmEmail = () => `<html><body>HERE IS MY TEMPLATE</body></html>`;
event.response = {
emailSubject: "Reset Password Notification",
emailMessage: postConfirmEmail()
};
callback(null, event);
}
};
As result, I am getting
InvalidLambdaResponseException: Unrecognizable lambda output (Error 400)
The thing is that password is really updating as expected, but e-mail confirmation is NOT sent and error remains.
TIP: User is CONFIRMED. Tried also with "context.done(null, event);" / "return event;" but response is always with the same error.
You shouldn't be using an async handler with the callback. It's either one or the other.
Simply return event without calling the callback or don't make your function async.
Additionally, Cognito will not send confirmation emails, you will have to implement this functionality yourself.

How to send an additional request to endpoint after each test case

I’m currently looking at Botium Box, and I’m wondering if it is possible to send an additional request to our endpoint after each test case? Let me give you some background information about how we set up the HTTP(S)/JSON connector in Botium Box and how we are sending information to our bot:
HTTP(S) endpoint:
https://MyChatBotsEndpoint.com/?userinput={{msg.messageText}}
HTTP method: POST
We also send cookies through the header template in the request builder. Like this:
{
"Cookie": "JSESSIONID={{context.sessionId}}"
}
The response is given back in JSON.
When a test ends (when it is successful but also when it fails), we need to send an additional request to our endpoint. The endpoint URL of that request should look like this:
https://MyChatBotsEndpoint.com/endsession
The header should include the cookie as described before.
Is there a way to achieve this in Botium?
Botium has many extension points to plug in your custom functionality. In this case, I guess the SIMPLEREST_STOP_HOOK is the best choice.
Write a small javascript file calling your endpoint, and register is with the SIMPLEREST_STOP_HOOK capability in botium.json. The context (session context from the HTTP/JSON connector) is part of the hook arguments.
in botium.json:
...
"SIMPLEREST_STOP_HOOK": "my-stop-hook.js"
...
my-stop-hook.js:
const request = require('request')
module.exports = ({ context }) => {
return new Promise((resolve, reject) => {
request({
method: 'GET',
uri: 'https://MyChatBotsEndpoint.com/endsession',
headers: {
Cookie: "JSESSIONID=" + context.sessionId
}
}, (err) => {
if (err) reject(err)
else resolve()
})
})
}

How do you test user flows that involve confirmation by email?

I mean functional or E2E testing. That's all clear with generic flows, but when it comes to transactional emails (signup confirmations, password resets, purchase notifications and others) it's still bringing questions. After some research I came up with a few ideas. One is to leverage Restmail.net API (here examples with Selenium WebDriver and Cypress - http://dsheiko.com/weblog/testing-sign-up-flow-with-activation-by-email). It's free, but API is public. So it's not really suitable for email messages with potentially sensitive information. Another approach to access Gmail inbox via IMAP bridge or Gmail API (here the explanation and code snippets - https://docs.puppetry.app/testing-emails/example-with-imap-bridge). But again, it's rather a workaround.
I know there are guys like Sendgrid, Mailgun, Email Yak, Postmark. I don't want to pay that much. So how do you folks do it? It it a thing to you?
We're doing this using Mailosaur email addresses for our test users. We then use a cypress custom command to query Mailosaur for the expected email. It was super easy to set up.
Here's the main part of that custom command, which is all we had to add to start doing email testing. You can refer to their API docs for what query, mailosaurServer, and MailosaurApiKey should be.
Cypress.Commands.add("getEmailFromMailService", query => {
return cy
.request({
method: "POST",
url: `https://mailosaur.com/api/messages/await?server=${mailosaurServer}`,
body: query,
headers: { "Content-Type": "application/json" },
auth: { user: mailosaurApiKey },
})
.then(response => {
expect(response.status).to.equal(200);
return response.body;
});
});
You could create a post request for the "forgot your password" and then assert on it.
something like:
cy.visit('yoursite')
cy.get('#forgotpassword').click().then(function (xhr) {
cy.server()
cy.request('POST', 'APIforForgotPassword').as('sucessfullemail)
})
cy.get(#sucessfullemail).then(function (xhr) {
expect(xhr.status).to.eq(200)
Cypress.Commands.add('ConfirmUser', () => {
const confirmationToken = null;
cy.request({
url: 'http://localhost:3000/api/confirmation_token?email=test_user#cypress.com',
followRedirect: false
})
.then((resp) => {
confirmationToken = resp.token
})
cy.visit('/en/confirmation?confirmation_token=token')
})
Create the API that requires the email as a parameter and returns the confirmation-token. call the API from cypress commands as ajax-request and get the response token

getaddrinfo ENOTFOUND API Google Cloud

I'm trying to execute API.AI tutorial for building a weather bot for Google Assistant (the one here: https://dialogflow.com/docs/getting-started/basic-fulfillment-conversation)
I made everything successfully, created the bot within API, created the Fulfillments, installed NodeJS on my pc, connected Google Cloud Platform, etc.
Then I created the index.js file by copying it exactly how it's stated on API.ai tutorial with my API key from World Weather Organisation (see below).
But when I use the bot, it doesn't work. On the Google Cloud Platform the error is always the same:
Error: getaddrinfo ENOTFOUND api.worldweatheronline.com
api.worldweatheronline.com:80
at errnoException (dns.js:28)
at GetAddrInfoReqWrap.onlookup (dns.js:76)
No matter how often I do it I get the same error. So I don't actually reach the API. I tried to see if anything changed from WWO side (URL, etc.) but apparently no. I updated NodeJS and still same issue. I refreshed the Google Cloud platform completely and didn't help.
That one I really can't debug. Could anyone help?
Here's the code from API.ai:
'use strict';
const http = require('http');
const host = 'api.worldweatheronline.com';
const wwoApiKey = '[YOUR_API_KEY]';
exports.weatherWebhook = (req, res) => {
// Get the city and date from the request
let city = req.body.result.parameters['geo-city']; // city is a required param
// Get the date for the weather forecast (if present)
let date = '';
if (req.body.result.parameters['date']) {
date = req.body.result.parameters['date'];
console.log('Date: ' + date);
}
// Call the weather API
callWeatherApi(city, date).then((output) => {
// Return the results of the weather API to Dialogflow
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': output, 'displayText': output }));
}).catch((error) => {
// If there is an error let the user know
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'speech': error, 'displayText': error }));
});
};
function callWeatherApi (city, date) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the weather
let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' +
'&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date;
console.log('API Request: ' + host + path);
// Make the HTTP request to get the weather
http.get({host: host, path: path}, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (d) => { body += d; }); // store each response chunk
res.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let forecast = response['data']['weather'][0];
let location = response['data']['request'][0];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
// Create response
let output = `Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of
${forecast['mintempC']}°C or ${forecast['mintempF']}°F on
${forecast['date']}.`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
res.on('error', (error) => {
reject(error);
});
});
});
}
Oh boy, in fact the reason was most stupid ever. I didn't enable "billing" on Google Cloud Platform and that's why it blocked everything (even though I'm using a free test of the API). They just wanted my credit card number. It works now
I had the same issue trying to hit my db. Billing wasn't the fix as I had billing enabled already.
For me it was knexfile.js setup for MySql - specifically the connection object. In that object, you should replace the host key with socketPath; and prepend /cloudsql/ to the value. Here's an example:
connection: {
// host: process.env.APP_DB_HOST, // The problem
socketPath: `/cloudsql/${process.env.APP_DB_HOST}`, // The fix
database: process.env.APP_DB_NAME,
user: process.env.APP_DB_USR,
password: process.env.APP_DB_PWD
}
Where process.env.APP_DB_HOST is your Instance connection name.
PS: I imagine that even if you're not using Knex, the host or server parameter of a typical DB connectionstring will have to be called socketPath when connecting to Google Cloud SQL.