So I first integrated Amadeus for flight booking and everything went well but problem arrived when I started integrating hotel booking. If I use nodejs library of Amadeus, I get a response that my access token is invalid. This is my code of nestjs service and response.
async hotelSearch(data) {
try {
var amadeus = new Amadeus({
clientId: process.env.API_KEY,
clientSecret: process.env.API_SECRET
});
return await amadeus.shopping.hotelOffers.get(data)
} catch (error) {
return error;
}
}
And this is the response.result -
"result": {
"errors": [
{
"code": 38190,
"title": "Invalid access token",
"detail": "The access token provided in the Authorization header is invalid",
"status": 401
}
]
},
"parsed": true
},
"description": [
{
"code": 38190,
"title": "Invalid access token",
"detail": "The access token provided in the Authorization header is invalid",
"status": 401
}
],
"code": "AuthenticationError"
}
How can I get Invalid Access Token error when I am using the library for it?? Anyway, after facing this issue I decided to use axios instead but still got no success.
async getToken(): Promise<{access_token: string}> {
try {
const data = qs.stringify({
client_id: process.env.API_KEY,
client_secret: process.env.API_SECRET,
grant_type: 'client_credentials'
});
const config:AxiosRequestConfig = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://test.api.amadeus.com/v1/security/oauth2/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
};
return await axios(config)
.then((response) => {
return((response.data));
})
.catch((error) => {
console.log(error);
});
} catch (error) {
return error;
}
}
async hotelSearch(data) {
try {
const tokenData=await this.getToken()
const refreshToken='Bearer '+ ( tokenData).access_token
console.log('token', refreshToken)
const config:AxiosRequestConfig = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://test.api.amadeus.com/v1/reference-data/locations/hotels/by-city',
data: data,
headers: {'Authorization':refreshToken},
};
return await axios(config).then((response) => {
return(JSON.stringify(response.data));
})
.catch((error) => {
return(error);
});
// var amadeus = new Amadeus({
// clientId: process.env.API_KEY,
// clientSecret: process.env.API_SECRET
// });
// return await amadeus.shopping.hotelOffers.get(data)
} catch (error) {
return error;
}
}
And this is the response I got this time.
{
"message": "Request failed with status code 400",
"name": "Error",
"stack": "Error: Request failed with status code 400\n at createError (E:\\travel-portal\\travel-portal\\node_modules\\axios\\lib\\core\\createError.js:16:15)\n at settle (E:\\travel-portal\\travel-portal\\node_modules\\axios\\lib\\core\\settle.js:17:12)\n at IncomingMessage.handleStreamEnd (E:\\travel-portal\\travel-portal\\node_modules\\axios\\lib\\adapters\\http.js:322:11)\n at IncomingMessage.emit (node:events:525:35)\n at endReadableNT (node:internal/streams/readable:1359:12)\n at processTicksAndRejections (node:internal/process/task_queues:82:21)",
"config": {
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": null,
"headers": {
"Accept": "application/json, text/plain, */*",
"Authorization": "Bearer CMHEjXBrpzE7YxF9O7GKygCtzCxO",
"Content-Type": "application/json",
"User-Agent": "axios/0.26.1",
"Content-Length": 117
},
"method": "get",
"url": "https://test.api.amadeus.com/v1/reference-data/locations/hotels/by-city",
"data": "{\"cityCode\":\"DEL\",\"radius\":\"5\",\"radiusUnit\":\"KM\",\"checkInDate\":\"2023-03-10\",\"checkOutDate\":\"2023-03-11\",\"adults\":\"2\"}"
},
"status": 400
}
I have cross-checked the payload. The bearer token is all good when I console it and even I checked the request with fiddler and there too header and data are getting passed. Any help to get this work through either of the method is really appreciated.
The amadeus.shopping.hotelOffers.get() endpoint has been decommissioned so you won't be able to use it. Install the latest version of the Node library and use the new Hotel Search endpoint as below:
var Amadeus = require(amadeus);
var amadeus = new Amadeus({
clientId: 'YOUR_API_KEY',
clientSecret: 'YOUR_API_SECRET'
});
// Get list of available offers in specific hotels by hotel ids
amadeus.shopping.hotelOffersSearch.get({
hotelIds: 'RTPAR001',
adults: '2'
}).then(function (response) {
console.log(response);
}).catch(function (response) {
console.error(response);
});
You can also check the migration guide for more details and how to use the new Hotel Search version.
Related
I am trying to request data from an endpoint in the twitter enterprise API (/totals)
I have been successfull in retrieving data from /historical using oauth 1.0a
And to my eye the response I get from the authorization looks correct
{
"statusCode": 403,
"body": {
"errors": [
"Forbidden to access tweets: 123456789,
]
},
"headers": {
"date": "Tue, 10 Aug 2021 12:17:56 GMT",
"server": "tsa_o",
"set-cookie": [
"personalization_id=\"XXXXXXX/xxxxxx==\"; Max-Age=63072000; Expires=Thu, 10 Aug 2023 12:17:57 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None",
"guest_id=v1%3A162859787755489778; Max-Age=63072000; Expires=Thu, 10 Aug 2023 12:17:57 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None"
],
"content-type": "application/json; charset=utf-8",
"x-request-id": "8db9ed7b269fe772",
"cache-control": "no-cache, no-store, max-age=0",
"content-length": "189",
"content-encoding": "gzip",
"strict-transport-security": "max-age=631138519",
"x-connection-hash": "3581ca69fa8583875f011a335e831ea0e9a1e6fbe14f2fb37d473825116bea23",
"connection": "close"
},
"request": {
"uri": "https://data-api.twitter.com/insights/engagement/totals",
"method": "POST",
"headers": {
"Accept-Encoding": "gzip",
"accept": "application/json",
"content-type": "application/json",
"content-length": 424,
"Authorization": "OAuth oauth_consumer_key=\"XXXXXXXXXXxxxxx\",oauth_nonce=\"xXXXXXX\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1628597877\",oauth_token=\"xxxxxxxXXXXXXX\",oauth_version=\"1.0\",oauth_signature=\"xXXXXXXXXXX\""
}
}
}
Code:
Authorization code below,
async getRequest({ oauth_token, oauth_token_secret }, params) {
const oAuthConfig = {
consumer_key: "xx",
consumer_secret: "xx",
token: oauth_token,
token_secret: oauth_token_secret,
}
const req = await post({
url: new URL(
"https://data-api.twitter.com/insights/engagement/totals"
),
headers: { "Accept-Encoding": "gzip" },
oauth: oAuthConfig,
body: params,
json: true,
gzip: true,
})
if (req.body) {
return req.body
} else {
throw new Error("Cannot get an OAuth request token")
}
}
This method is then called as per docs with the information needed, however as you can see from the output I get Forbidden to access tweets,
async getMetrics(tweet_ids, info, oldest) {
const accessToken = {
oauth_token: info.tokens.accessToken,
oauth_token_secret: info.tokens.refreshToken,
user_id: info.id,
screen_name: info.username,
}
try {
let params = {
tweet_ids: tweet_ids,
engagement_types: [
"impressions",
"engagements",
// "favorites",
// "retweets",
// "quote_tweets",
// "replies",
// "video_views"
],
groupings: {
"types-by-tweet-id": {
group_by: [
"tweet.id",
"engagement.type",
],
},
},
}
const response = await this.getRequest(accessToken, params)
console.log(response)
if (response.hasOwnProperty("errors")) {
if (response.unavailable_tweet_ids) {
}
throw response.errors
}
return response
} catch (e) {
console.error(e)
throw e
}
}
I am at a total loss to why this is not working, as it works like I described with the /historical endpoint, can anyone spot the error in the authentication code as per docs Forbidden access means per docs that there is something wrong with the authentication
Docs: https://developer.twitter.com/en/docs/twitter-api/enterprise/engagement-api/api-reference/post-insights-engagement#errors
I'm struggling a bit within a small project for fetching and creating (via POST) an entry, where I have on one side:
A GraphQL server (apollo)
A react app, using useQuery hook
A rest API, where the resolvers of the Apollo project is fetching data with async JS functions
I have the following obstacles:
I'm not able to post an entry for the rest API via GraphQl query or Mutation.
I have success in this post request:
POST https://technologytalents.io/space-cats/index.php/openapi/create_channel_entry
Accept: application/json
Content-Type: application/x-www-form-urlencoded
User-Agent: axios/0.21.1
channel_id=1&url_title=Blas&entry_date=12345678&title=Dooom&session_id=b288ea559b20c584a3a793685ceb20c240c26569
The success response of this is:
{entry_id: 2}
In my graphQL schema:
input entryIntput {
url_title: String
title: String
channel_id: Int
entry_date: Int
}
type postEntrySuccess {
entry_id: Int
}
type Mutation {
createEntry(input: entryIntput): postEntrySuccess
}
and in the resolvers:
Mutation: {
createEntry: async (_, entry) => await channelEntriesService.postEntry(entry)
}
my ChannelEntriesSerives looks like:
const axios = require('axios')
const authenticate = require('./authenticate')
class ChannelEntries {
constructor(options) {
this._options = options
}
async getEntries() {
const auth = await authenticate.auth()
const patch = {
...options,
url: `${options.url}/get_channel_entries?channel_id=1&where[status]=open&session_id=${auth.session_id}`
}
const response = await axios(patch)
return response.data
}
async postEntry(entry = { url_title: 'Blas', title: 'Dooom', entry_date: Date.now(), channel_id: 1 }) {
const auth = await authenticate.auth()
const patch = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
url: `${this._options.url}/create_channel_entry?channel_id=${entry.channel_id}&url_title=${entry.url_title}&title=${entry.title}&entry_date=${entry.entry_date}_id=${auth.session_id}`
}
const response = await axios.request(patch)
return response.data
}
}
const options = {
method: 'GET',
url: 'https://technologytalents.io/space-cats/index.php/openapi',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}
module.exports = instance = new ChannelEntries(options)
When I try to execute the mutation on the GraphQl studio:
mutation CreateEntry($createEntryInput: entryIntput) {
createEntry(input: $createEntryInput) {
entry_id
}
}
I've got an error:
{
"errors": [
{
"message": "Request failed with status code 400",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createEntry"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"config": {
"url": "https://technologytalents.io/space-cats/index.php/openapi/create_channel_entry?channel_id=undefined&url_title=undefined&title=undefined&entry_date=undefined_id=b3c77d7c74b0cc10de61c90f8e1a34b30e454f7a",
"method": "post",
"headers": {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "axios/0.21.1"
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
}
}
}
}
],
"data": {
"createEntry": null
}
}
What I'm doing wrong?
I found the reason for the error, and this is due to my rusty Axios basics. The config of an Axios request should have "data" property, so changing it to
const patch = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
url: `${this._options.url}/create_channel_entry`,
data: `channel_id=${entry.channel_id}&url_title=${entry.url_title}&title=${entry.title}&entry_date=${entry.entry_date}&session_id=${auth.session_id}`
}
returns the correct response.
The other issue is just a correct mapping of the response with graphql schema.
I'm trying to build react-native fuction to make POST request to an API using Axios. The function seems to work but i keep getting the response as undefined. I've tested POST request with Postman, it works normally.
{
"CaThuHoiList": [
{
"NGAY": "20-01-2020",
"TH_ID": 33,
"CA": 1,
"CT_DONVI_ID": 78,
"CREATE_BY": 4797,
"TRANG_THAI": "HT",
"CREATE_DATE": "20-01-2020 00:00",
"KHO_ID": null,
"LOAI": "TT"
},
{
"NGAY": "20-01-2020",
"TH_ID": 34,
"CA": 1,
"CT_DONVI_ID": 78,
"CREATE_BY": 4797,
"TRANG_THAI": "BD",
"CREATE_DATE": "20-01-2020 00:00",
"KHO_ID": null,
"LOAI": "TL"
}
],
"Status": 1,
"Message": ""
}
However i couldn't get the same response (undefined) while running my function:
onPostJson = () => {
axios.post('https://10.1.127.17:11111/vpdu/get-ca-thu-hoi', {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
FromDate: "01-Jan-2020",
ToDate: "01-Feb-2020",
Ca: 1
})
})
.then(function (response) {
data = response;
console.log(data); \\Storing response in data still getting undefined
return response;
})
.catch(function (error) {
console.log(error);
});
}
Could anyone tell me what is the problem? Thanks in advance.
You need to have data pulled out. I have done the changes. You may check out.
onPostJson = () => {
let configObject = {
"url": "https://10.1.127.17:11111/vpdu/get-ca-thu-hoi",
"method": "post",
"headers": {
'Content-Type': 'application/json'
},
"data":{
"FromDate": "01-Jan-2020",
"ToDate": "01-Feb-2020",
"Ca": 1
}
}}
axios.request(configObject ).then((res) => {
console.log("react1: ", res);
console.log("react2: ", res.data);
})
}
const inserrequest=async () => {
let addreq = await axios.post('http://xxxxxx..//Employee/PostInsertrequest',
{
ReqEmpCode: 'xxxxxx',
});
console.warn(JSON.stringify(addreq.data));
}
I am calling the google fit rest api as following
const requestBody ={
"aggregateBy": [{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}],
"bucketByTime": { "durationMillis": 86400000 },
"startTimeMillis": 1561228200000,
"endTimeMillis": 1561652514300
}
const userAction = async () => {
const response = await fetch('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate', {
method: 'POST',
body: requestBody,
headers: {
'Content-Type': 'application/json',
'Content-Length': '302',
'Authorization': 'Bearer ' + authcode,
}
});
const jsonResponse = await response.json();
console.log(jsonResponse);
}
userAction();
I am getting response as
{
"error": {
"errors": [{
"domain": "global",
"reason": "parseError",
"message": "Parse Error"
}],
"code": 400,
"message": "Parse Error"
}
}
Not sure where the parsing error is happening. Any help pointing to where it occours will be much appreciated.
Note - auth token is taken correctly, so it probably cant be the issue.
Also I am running on local host.
Got it to work by the following code(I have hardcoded static params). Add api key and client ID and run it would work.
This link helped - https://developers.google.com/fit/rest/v1/reference/users/dataset/aggregate.
<script>
function authenticate() {
return gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/fitness.activity.read"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
function loadClient() {
gapi.client.setApiKey("YOUR APP ID");
return gapi.client.load("https://content.googleapis.com/discovery/v1/apis/fitness/v1/rest")
.then(function() { console.log("GAPI client loaded for API"); },
function(err) { console.error("Error loading GAPI client for API", err); });
}
// Make sure the client is loaded and sign-in is complete before calling this method.
function execute() {
return gapi.client.fitness.users.dataset.aggregate({
"userId": "me",
"resource": {
"aggregateBy": [
{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}
],
"endTimeMillis": 1561652514300,// Any time in milliseconds
"startTimeMillis": 1561228200000,// Any time in milliseconds
"bucketByTime": {
"durationMillis": 86400000// Any duration in milliseconds
}
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
gapi.load("client:auth2", function() {
gapi.auth2.init({client_id: "YOUR CLIENT ID"});
});
</script>
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>
I will try to describe my problem as the best I can. I have a method who (1)get all Grafana datasources via the API (2)create 2 JSON (3)post them to Grafana API.
The thing is sometimes one of my JSON is not finish when the code make the 3rd step so I got an error and the result is not what I want.
updateDashboard = Meteor.bindEnvironment(function() {
console.log("called updateDashboard()");
new Promise(Meteor.bindEnvironment(function(resolve) {
// get all the datasources of Grafana
HTTP.call("GET", 'http://localhost:3000/api/datasources', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': APIKEY,
},
},
function(error, result) {
if (!error) {
allDataSources = result.data;
resolve(allDataSources);
} else {
console.error(error);
}
});
})).then(function(allDataSources) {
// create the dashboard
return new Promise(function(resolve) {
//ANCHOR
var dataJSON = {
//create the dashboard
"annotations": {
"list": []
},
"description": "Containers metrics",
"editable": true,
"gnetId": null,
"graphTooltip": 1,
"hideControls": false,
"id": null,
"links": [],
"refresh": "1s",
//create the lines
"rows": _.map(allDataSources, function(ds, index) {
return newGraphOverview(ds, index);
}),
//dashboard things
"schemaVersion": 14,
"style": "dark",
"tags": [
"docker"
],
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"1s",
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "browser",
"title": "Docker Containers _custom_from_js",
"version": 1
}
dataJSONDetails = newGraphDetails(allDataSources);
resolve(dataJSON);
});
}).then(Meteor.bindEnvironment(function(dataJSON) {
// send the dashboard
HTTP.call("POST", "http://localhost:3000/api/dashboards/db", {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': APIKEY,
},
data: {
dashboard: dataJSON,
overwrite: true
}
},
function(error, result) {
if (!error) {
console.log("result post " + dataJSON.title + " ----------------------------------")
console.log(result);
} else {
//HERE I WANT TO DO IF ERROR GO TO ANCHOR
console.log("error post " + dataJSON.title + " ----------------------------------")
console.error(error);
}
});
//send the dashboard for details view
HTTP.call("POST", "http://localhost:3000/api/dashboards/db", {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': APIKEY,
},
data: {
dashboard: dataJSONDetails,
overwrite: true
}
},
function(error, result) {
if (!error) {
console.log("result post " + dataJSONDetails.title + " ----------------------------------")
console.log(result);
} else {
//HERE I WANT TO DO IF ERROR GO TO ANCHOR
console.log("error post " + dataJSONDetails.title + " ----------------------------------")
console.error(error);
}
});
}));
});
I know the code snipped doesn't work but it was easier for me to make it readable.
So someone could help me to make a thing like "if error try to create again" ? I have seen Meteor retries but it don't think it's what I need
You shouldn't use promises or callbacks. Write normal, synchronous-looking code as you've seen in Meteor examples.
var datasources = null;
try {
var request1 = HTTP.call("GET", 'http://localhost:3000/api/datasources', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': APIKEY,
}
});
// From http://docs.meteor.com/api/http.html#HTTP-call
datasources = request.data;
} catch (e) {
console.error(e);
return;
}
// Do all the other steps you keep wanting to do...
Repeat a similar statement for your other HTTP calls. Generally, you will not be throwing errors unless localhost is unreachable, in which case everything is unreachable. So don't bother with try-catch. Let the exception be thrown and handled by the client (the caller of the method).
You shouldn't generally have to use Meteor.bindEnvironment.