NodeJS Https.request issue - javascript

I am trying to write a test post to the Zip API:
Zip Api
JSON posted, as per Zip Pay's example -
{
"shopper": {
"title": "Mr",
"first_name": "John",
"last_name": "Smith",
"middle_name": "Joe",
"phone": "0400000000",
"email": "test#emailaddress.com",
"birth_date": "2017-10-10",
"gender": "Male",
"statistics": {
"account_created": "2015-09-09T19:58:47.697Z",
"sales_total_number": 2,
"sales_total_amount": 450,
"sales_avg_value": 250,
"sales_max_value": 350,
"refunds_total_amount": 0,
"previous_chargeback": false,
"currency": "AUD"
},
"billing_address": {
"line1": "10 Test st",
"city": "Sydney",
"state": "NSW",
"postal_code": "2000",
"country": "AU"
}
},
"order": {
"reference": "testcheckout1",
"amount": 200,
"currency": "AUD",
"shipping": {
"pickup": false,
"tracking": {
"uri": "http://tracking.com?code=CBX-343",
"number": "CBX-343",
"carrier": "tracking.com"
},
"address": {
"line1": "10 Test st",
"city": "Sydney",
"state": "NSW",
"postal_code": "2000",
"country": "AU"
}
},
"items": [
{
"name": "Awesome shoes",
"amount": 200,
"quantity": 1,
"type": "sku",
"reference": "1"
}
]
},
"config": {
"redirect_uri": "http://www.redirectsuccess.com/zipmoney/approved"
},
"metadata": {
"name1": "value1"
}
}
but I am getting an error message:
{"error":{"code":"request_invalid","message":"There are errors with the request, please see error items for more detail","details":[{"name":"Amount","message":"'Amount' must be greater than '0'."},{"name":"Currency","message":"Currency must be either 'AUD' or 'NZD'"},{"name":"Authority","message":"'Authority' must not be empty."}]}}
Cant figure out what is wrong. These elements exist in the data sent. Have used Axios and the request comes back fine. But Node's HTTPS object doesnt work. Why? :(
TIA
const postData = JSON.stringify(data);
let options = {
"host": 'api.sandbox.zipmoney.com.au',
"path": '/merchant/v1/charges',
"method": 'POST',
"port": 443,
"encoding": "utf8",
"headers": {
'Authorization': 'Bearer test',
'Content-Type': 'application/json',
'Content-Length': postData.length,
'Zip-Version': '2017-03-01'
}
};
const req = https.request(options, (response)=>{
console.log(`statusCode: ${response.statusCode}`)
var str = ''
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log("have end data")
console.log(str);
});
})
req.on('error', error => {
console.log("zip errors...")
console.error(error)
})
req.write(postData)
req.end();

I had some time to take another look at your question, today, but still haven't found a definite solution to your specific issue. While this post may not answer your question, I wanted to post my findings here.
Here is the code that I tried (based on your code) on my side using a different test API:
// ref: https://stackoverflow.com/q/66218603/1167750
// ref: https://attacomsian.com/blog/node-http-post-request
const https = require('https');
let data = {
"title": "test",
"body": "Content",
"userId": 1
}
const postData = JSON.stringify(data);
let options = {
"host": 'jsonplaceholder.typicode.com',
"path": '/posts',
"method": 'POST',
"port": 443,
"encoding": "utf8",
"headers": {
'Content-Type': 'application/json',
'Content-Length': postData.length,
'Zip-Version': '2017-03-01'
}
};
const req = https.request(options, (response)=>{
console.log(`statusCode: ${response.statusCode}`)
var str = ''
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log("have end data")
console.log(str);
});
})
req.on('error', error => {
console.log("zip errors...")
console.error(error)
})
req.write(postData)
req.end();
When I run this with Node, I get the output:
$ node test.js
statusCode: 201
have end data
{
"title": "test",
"body": "Content",
"userId": 1,
"id": 101
}
So I'm able to get a normal response back from this other API while using a lot of your code from your post. This seems to show that your approach with using Node's https module works for another API at least. Maybe this can help you narrow down where the issue is based on this test information.
Additionally, from your example json data, I don't see a place where the Authority field is filled out in your request. The error message you got indicated that it was required and the related documentation seems to require that too. But maybe this was simply removed from your json data before it was posted in your question for security reasons?
Seeing that you do have the amount and currency values set in your json order data, that seems to indicate that the API is not recognizing your json data that is being sent. But hearing that the axios approach works makes this issue still confusing.
Though it might not be necessary, I also checked your json data in a linter and it came back as valid (as you already know). Wanted to double check if there was some issue related to that, though.

Related

Put items in DynamoDB without knowing the attributes

This feels like a really stupid question, but my lack of JS knowledge combined with lack of AWS knowledge has me in a tight spot! I'm just trying to get to grips with a basic AWS stack i.e. Lambda/Dynamo/API Gateway for some basic API work.
If I want a simple API endpoint to handle PUT requests e.g. https://my.endpoint.amazonaws.com/users. If I have a DynamoDB table with a composite primary key of userID and timestamp I could use the code snippet below to take the unknown data (attributes that weren't known when setting a schema), but this obviously doesn't work well
const dynamo = new AWS.DynamoDB.DocumentClient();
let requestJSON = JSON.parse(event.body);
await dynamo
.put({
TableName: "myDynamoTable",
Item: {
userID: requestJSON.userID,
timestamp: requestJSON.timestamp,
data: requestJSON.data
}
})
.promise();
I could send a PUT request with the following JSON
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
}
but then address and name get shoved into a single DynamoDB attribute named data. How do I construct the node code to create a new attribute named address and one named name with the corresponding values, if I didn't know these attributes i.e. I want to use JSON in my request like below, but assuming I don't know this and can't use requestJSON.address
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"address": "Elm Street",
"name": "Glen"
}
You can use the spread operator, for example:
const data = {
address: "Elm Street",
name: "Glen",
};
const item = {
userID: "aaaa1111",
timestamp: 1649677057,
...data,
};
console.log("item:", item);
If you insist on the API contract as
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
}
then you can do mapping on the TypeScript level
const request: {userID: string, timestamp: number, data: any} = {
"userID": "aaaa1111",
"timestamp": 1649677057,
"data": {
"address": "Elm Street",
"name": "Glen"
}
};
const ddbObject: any = {
"userID": request.userID,
"timestamp": request.timestamp
};
Object
.keys(request.data)
.forEach(key => ddbObject[key] = request.data[key]);
Representation of ddbObject is
{
"userID": "aaaa1111",
"timestamp": 1649677057,
"address": "Elm Street",
"name": "Glen"
}

Issues adding sessions and information to the Google Fit REST Api using JS

So I am reasonably new to using API's with Js but I am struggling a lot to understand how the Google Fit API works. I am attempting to add a new Workout's data to the API by adding a session and some data for the intensity (heart points) of the session. I can get the session to appear correctly but run into constant errors when I try to create a dataSource and add a point to it for the session. It would be greatly appreciated if someone could help me to fix my code to achieve this or could direct me to a more thorough example of similar code as the API docs don't seem to be too well detailed with examples etc. Thanks in advance.
Here's the 3 api calls that I have written so far, one for creating the DataSource, one for the DataPoint and one for the Session. The session works correctly and adds a session of 1 hr for the correct activity but I am unable to get any of the other API requests to work.
Data Source :
``gapi.client.fitness.users.dataSources.create({
"userId":"me",
"resource": {
"application": {
"name": "LittleWorkouts"
},
"dataType": {"field":[{
"format": "floatPoint",
"name": "com.google.heart_minutes"
}],
"name": "com.google.heart_minutes"
},
"device": {
"manufacturer": "op",
"model": "6",
"type": "phone",
"uid": "1000019",
"version": "1"
},
"type": "raw"
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error 1", err); });
``
Data Point :
``
gapi.client.fitness.users.dataSources.datasets.patch({
"dataSourceId":"raw:com.google.heart_minutes:292824132082:op:6:1000019",
"userId": "me",
"datasetId": "1592087806561000000-1592287806561000000",
"resource": {
"minStartTimeNs": "1592087806561000000",
"maxEndTimeNs": "1592287806561000000",
"dataSourceId": "raw:com.google.heart_minutes:292824132082:op:6:1000019",
"point": [
{
"startTimeNanos": "1592087806561000000",
"endTimeNanos": "1592287806561000000",
"value": [
{
"fpVal": 89.1
}
],
"dataTypeName": "com.google.heart_minutes"
}
]
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error 2", err); });
``
Session :
``gapi.client.fitness.users.sessions.update({
"userId":"me",
"sessionId": "someSessionId19",
"id": "someSessionId19",
"name": "Awesome Workout19",
"description": "A very intense workout",
"startTimeMillis": new Date().getTime() - 3600000,
"endTimeMillis": new Date().getTime(),
"version": 1,
"lastModifiedToken": "exampleToken",
"application": {
"detailsUrl": "http://example.com",
"name": "LittleWorkouts",
"version": "1.0"
},
"activityType": 21,
"activeTimeMillis": 3600000
}).then((res) => {console.log(res)});
console.log('res')
//request.execute((res) => {console.log(res);console.log('executrd')})
console.log(auth2.currentUser.get().getBasicProfile().getGivenName());
var request2 = gapi.client.fitness.users.sessions.list({
"userId":"me"
}).then((res) => {console.log(res)})
``
Error message
{message: "Unable to fetch DataSource for Dataset: raw:com.google.heart_minutes:292824132082:op:6:1000019", domain: "global", reason: "invalidArgument"}
It looks like it could be that you're trying to pass in the wrong fields for the data type: if you want to use a standard data type (like com.google.heart_minutes), you should either pass the exact fields of the standard data type (the field should be called "intensity"); or just pass the data type name, and the backend will fill them in for you.
So, if you change the data type to
"dataType": {"name": "com.google.heart_minutes"}
It should work.
Then, you need to use the data source ID returned from that request for the data points.
Awesome, so after some support in the comments I have some working code to add a new session with data from a previously defined data source using 3 API calls. The first call is to create a data source and only needs to be run once. The second and third then add a data point to a data set and creates a new session for the workout respectively. Here's the final working code:
Data Source:
/*
gapi.client.fitness.users.dataSources.create({
"userId":"me",
"resource": {
"application": {
"name": "LittleWorkouts"
},
"dataType": {
"name": "com.google.heart_minutes"
},
"device": {
"manufacturer": "op",
"model": "6",
"type": "phone",
"uid": "1000020",
"version": "1"
},
"type": "raw"
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error 1", err); });
*/
Data and Data Set:
gapi.client.fitness.users.dataSources.datasets.patch({
"dataSourceId":"raw:com.google.heart_minutes:108881196053:op:6:1000020",
"userId": "me",
"datasetId": z,
"resource": {
"minStartTimeNs": workoutStartTime * 1000000,
"maxEndTimeNs": workoutEndTime * 1000000,
"dataSourceId": "raw:com.google.heart_minutes:108881196053:op:6:1000020",
"point": [
{
"originDataSourceId": "raw:com.google.heart_minutes:108881196053:op:6:1000020",
"value": [
{
"fpVal": 8
}
],
"dataTypeName": "com.google.heart_minutes",
"endTimeNanos": workoutEndTime * 1000000,
"startTimeNanos": workoutStartTime * 1000000,
}
]
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error 2", err); });
Session:
gapi.client.fitness.users.sessions.update({
"userId":"me",
"sessionId": id,
"id": id,
"name": "Morning Workout",
"description": "A very intense workout",
"startTimeMillis": workoutStartTime,
"endTimeMillis": workoutEndTime,
"version": 1,
"lastModifiedToken": "exampleToken",
"application": {
"detailsUrl": "http://example.com",
"name": "LittleWorkouts",
"version": "1.0"
},
"activityType": 21,
"activeTimeMillis": workoutEndTime - workoutStartTime
}).then((res) => {console.log(res)});
console.log('res')

How to pull response from a URL and than put said response into a JSON

I need to send a request to https://authserver.mojang.com/authenticate
and get the response
{
"accessToken": "valid accessToken",
"clientToken": "client identifier",
"selectedProfile": {
"id": "profile identifier",
"name": "player name"
},
"requestUser": true
}
But I dont know how to ask the server for the out put.
What I need to input is
{
"agent": { "name": "Minecraft", "version": 1 },
"username": "-email here-",
"password": "-pass here-"
}
That is the data needed to get the token but I can't seem to get the code to ask the server for the token data and than receive it and put it into a JSON.
const postData = async _ => {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
"agent": { "name": "Minecraft", "version": 1 },
"username": "-email here-",
"password": "-pass here-"
})
})
console.log(response)
}
This should get the response you need judging by https://wiki.vg/Authentication#Payload this link.

The JSON my function POST's is different to what I pass to it

I have this function which is meant to POST JSON obtained from a GitHub API call:
postToDb: function(body) {
var http = require("http");
var options = {
hostname: 'localhost',
port: 9000,
path: '/issues',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: body
};
var req = http.request(options, function(res) {
console.log('Status: ' + res.statusCode);
console.log('Headers: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (body) {
console.log('Body: ' + body);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write('{"string": "Hello, World"}');
req.end();
}
However something like {"_id":"5b7c6d0457de7d6c7c4ae85b","__v":0} gets POST'ed instead. This is what I expected:
{
"url": "https://api.github.com/repos/TestOrg/test2/issues/4",
"repository_url": "https://api.github.com/repos/TestOrg/test2",
"labels_url": "https://api.github.com/repos/TestOrg/test2/issues/4/labels{/name}",
"comments_url": "https://api.github.com/repos/TestOrg/test2/issues/4/comments",
"events_url": "https://api.github.com/repos/TestOrg/test2/issues/4/events",
"html_url": "https://github.com/TestOrg/test2/issues/4",
"id": 347593311,
"node_id": "MDU6SXNzdWUzNDc1OTMzMTE=",
"number": 4,
"title": "test issue 2",
"user": {
"login": "my-repo",
"id": 32067576,
"node_id": "MDQ6VXNlcjMyMDY3NTc2",
"avatar_url": "https://avatars1.githubusercontent.com/u/32067576?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/my-repo",
"html_url": "https://github.com/my-repo",
"followers_url": "https://api.github.com/users/my-repo/followers",
"following_url": "https://api.github.com/users/my-repo/following{/other_user}",
"gists_url": "https://api.github.com/users/my-repo/gists{/gist_id}",
"starred_url": "https://api.github.com/users/my-repo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/my-repo/subscriptions",
"organizations_url": "https://api.github.com/users/my-repo/orgs",
"repos_url": "https://api.github.com/users/my-repo/repos",
"events_url": "https://api.github.com/users/my-repo/events{/privacy}",
"received_events_url": "https://api.github.com/users/my-repo/received_events",
"type": "User",
"site_admin": false
},
"labels": [],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [],
"milestone": null,
"comments": 0,
"created_at": "2018-08-04T06:34:50Z",
"updated_at": "2018-08-04T06:34:50Z",
"closed_at": null,
"author_association": "CONTRIBUTOR",
"body": "In order to help resolve your issue as quickly as possible please provice the following information when \r\n\r\n1. What is the issue?\r\nAnother one!\r\n\r\n2. What are the steps to recreate the issue?\r\n\r\n\r\n3. Do you have any relevant code samples? If so, please provice them.\r\n\r\n\r\n4. Have you made any specific configurations?\r\n\r\n\r\n5. Do you get an error? If so please include it and any stack trace.\r\n\r\n\r\n6. What version of the API are you using?\r\n",
"performed_via_github_app": null,
"score": 1
}
It seems getGitHubIssues.postToDb(issueFormatForDb); passes the correct JSON but the body inside var req is different. I'm sure this is just something I don't yet understand. Could somebody please explain what is happening here and how I could change this function to POST the correct JSON?
See comments above. I had updated the JSON I'm POST'ing but forgot to update my schema.

firebase "long link is not parsable"

I want to shorten a longLink with firebase and the REST API but I get the following response and I don't know what is wrong:
Response:
{
"error": {
"code": 400,
"message": "Long link is not parsable: https://www.google.de [https://firebase.google.com/docs/dynamic-links/rest#create_a_short_link_from_parameters]",
"status": "INVALID_ARGUMENT"
}
}
And this is how I do it:
The Request: https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=(hereismyapikey)
the Body lookes like this:
{
"longDynamicLink": "https://www.google.de",
"suffix": {
"option": "SHORT"
}
}
I tried first with the real URL I want to shorten. Same error. Than with google and with and without the http(s). I'm out of options and hope somebody sees what I did wrong here.
EDIT: Full Postman request:
"item": [
{
"name": "shortLinks",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"longDynamicLink\": \"www.google.de\",\r\n \"suffix\": {\r\n \"option\": \"SHORT\"\r\n }\r\n}"
},
"url": {
"raw": "https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=xxx",
"protocol": "https",
"host": [
"firebasedynamiclinks",
"googleapis",
"com"
],
"path": [
"v1",
"shortLinks"
],
"query": [
{
"key": "key",
"value": "xxx"
}
]
}
},
"response": []
}
]
You are using the simple method for creating dynamic link which is roughly equals to the manual creation of dynamic link : https://firebase.google.com/docs/dynamic-links/create-manually
In the docs if you see the link passed in example carefully you will see the pattern as below:
https://your_subdomain.page.link/?link=your_deep_link&apn=package_name[&amv=minimum_version][&afl=fallback_link]
So you should format the input link according to this or create using the parameters which has very good breakdown of parameters in json:
https://firebase.google.com/docs/dynamic-links/rest#create_a_short_link_from_parameters
Here is the api reference for firebase dynamic link creation from parameters:
https://firebase.google.com/docs/reference/dynamic-links/link-shortener#parameters
I find the JSON parameter method is easier.
var body = {
"dynamicLinkInfo": {
"dynamicLinkDomain": "yourcustom.page.link",
"link": fileUrl
},
"suffix": {
"option": "SHORT"
}
};
Then if you're using Node. The node-fetch package REST call would work like:
var fetchFileUrl = fetch(YOUR_SHORTLINK_URL, {
method: 'POST',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
}).then(function(response){
return response.json();
});

Categories

Resources