KuCoin API - TypeError: request.charAt is not a function - node.js

I'm trying to make a request to the KuCoin API to query the balance. I'm using the NodeJS API found here but I keep getting the error whenever I execute the code.
And here's the code snippet
data().then(api => {
const apiKey = api.api_key;
const apiSecretKey = api.api_secret;
const contactId = api.contact_id;
const exchange = api.exchange;
const passphrase = 'Passphrase';
/** Init Configure */
const config =
{
key: apiKey, // KC-API-KEY
secret: apiSecretKey, // API-Secret
passphrase: passphrase, // KC-API-PASSPHRASE
environment: "live"
}
API.init(require(config));
if (apiKey && exchange === "KuCoin-Futures") {
console.log("KuCoin Balance")
async function getBalance() {
try {
let r = await API.getAccountOverview()
console.log(r.data)
} catch(err) {
console.log(err)
}
}
return getBalance()
}
});
I the console log I get the following error
TypeError: request.charAt is not a function
at Function.Module._resolveLookupPaths (internal/modules/cjs/loader.js:617:15)
Does anyone know how I can fix this??

There are couple of things which look weird in the code snippet you provided, but the sample code from the kucoin-node-api library you linked should work perfectly fine. In case you are using that one, try this snippet which should show your account info:
const api = require('kucoin-node-api');
const config = {
apiKey: 'YOUR_KUCOIN_API_KEY',
secretKey: 'YOUR_KUCOIN_API_SECRET',
passphrase: 'YOUR_KUCOIN_API_PASSPHRASE',
environment: 'live'
};
api.init(config);
api.getAccounts().then((r) => {
console.log(r.data);
}).catch((e) => {
console.log(e);
});
In case you're using a different library, kucoin-node-sdk maybe (judging by your code snippet), then try to configure it correctly:
config.js file:
module.exports = {
baseUrl: 'https://api.kucoin.com',
apiAuth: {
key: 'YOUR_KUCOIN_API_KEY',
secret: 'YOUR_KUCOIN_API_SECRET',
passphrase: 'YOUR_KUCOIN_API_PASSPHRASE'
},
authVersion: 2
}
and your main.js (or whatever the name is):
const API = require('kucoin-node-sdk');
API.init(require('./config'));
const main = async () => {
const getTimestampRl = await API.rest.Others.getTimestamp();
console.log(getTimestampRl.data);
};
main();
The code above will show you KuCoin server timestamp only, but should be enough to keep going.
Good luck with trading!

Related

Correctly fetch authentication tokens server-side in a node.js React app hosted on Cloud Run

While not a front-end developer, I'm trying to set up a web app to show up a demo for a product. That app is based on the Sigma.js demo app demo repository.
You'll notice that this app relies on a graph which is hosted locally, which is loaded as:
/src/views/Root.tsx :
useEffect(() => {
fetch(`${process.env.PUBLIC_URL}/dataset.json`)
.then((res) => res.json())
.then((dataset: Dataset) => {...
// do things ....
and I wish to replace this by a call to another service which I also host on Cloud Run.
My first guess was to use the gcloud-auth-library, but I could not make it work - especially since it does not seem to support Webpack > 5 (I might be wrong here), the point here this lib introduces many problems in the app, and I thought I'd be better off trying the other way GCP suggests to handle auth tokens: by calling the Metadata server.
So I replaced the code above with:
Root.tsx :
import { getData } from "../getGraphData";
useEffect(() => {
getData()
.then((res) => res.json())
.then((dataset: Dataset) => {
// do even more things!
getGraphData.js :
import { getToken } from "./tokens";
const graphProviderUrl = '<my graph provider service URL>';
export const getData = async () => {
try {
const token = await getToken();
console.log(
"getGraphData.js :: getData : received token",
token
);
const request = await fetch(
`${graphProviderUrl}`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const data = await request.json();
console.log("getGraphData.js :: getData : received graph", data);
return data;
} catch (error) {
console.log("getGraphData.js :: getData : error getting graph data", error);
return error.message;
}
};
tokens.js :
const targetAudience = '<my graph provider service base URL>'; // base URL as audience
const metadataServerAddress = "169.254.169.254"; // use this to shortcut DNS call to metadata.google.internal
export const getToken = async () => {
if (tokenExpired()) {
const token = await getValidTokenFromServer();
sessionStorage.setItem("accessToken", token.accessToken);
sessionStorage.setItem("expirationDate", newExpirationDate());
return token.accessToken;
} else {
console.log("tokens.js 11 | token not expired");
return sessionStorage.getItem("accessToken");
}
};
const newExpirationDate = () => {
var expiration = new Date();
expiration.setHours(expiration.getHours() + 1);
return expiration;
};
const tokenExpired = () => {
const now = Date.now();
const expirationDate = sessionStorage.getItem("expirationDate");
const expDate = new Date(expirationDate);
if (now > expDate.getTime()) {
return true; // token expired
}
return false; // valid token
};
const getValidTokenFromServer = async () => {
// get new token from server
try {
const request = await fetch(`http://${metadataServerAddress}/computeMetadata/v1/instance/service-accounts/default/token?audience=${targetAudience}`, {
headers: {
'Metadata-Flavor': 'Google'
}
});
const token = await request.json();
return token;
} catch (error) {
throw new Error("Issue getting new token", error.message);
}
};
I know that this kind of call will need to be done server-side. What I don't know is how to have it happen on a React + Node app. I've tried my best to integrate good practices but most questions related to this topic (request credentials through a HTTP (not HTTPS!) API call) end with answers that just say "you need to do this server-side", without providing more insight into the implementation.
There is a question with similar formulation and setting here but the single answer, no upvote and comments is a bit underwhelming. If the actual answer to the question is "you cannot ever call the metadata server from a react app and need to set up a third-party service to do so (e.g. firebase)", I'd be keen on having it said explicitly!
Please assume I have only a very superficial understanding of node.js and React!

Issue leveraging keycloak-verify to instantiate keyclock object verifier

I'm leveraging this library here (https://www.npmjs.com/package/keycloak-verify)
For some reason.. requiring the class and then trying to instantiate gives me an error
TypeError: Keycloak is not a function
My code is below, I have also tried to print out the Keycloak object from the const and it gives me
{ default: [Function: Keycloak] }
Shouldn't just calling Keycloak() leverage that default function instead of giving me an error?
Thanks!
const Keycloak = require('keycloak-verify');
const run = async() => {
console.log(Keycloak)
const config = { realm: 'users', authServerUrl: 'localhost://8080' };
const keycloak = Keycloak(config);
const accessToken = '12312';
try {
const user = await keycloak.verifyOnline(accessToken);
console.log(user);
} catch(e) {
console.log(e);
}
}
run();
Looking at the compiled index.js it shows
var Keycloak = function Keycloak(config) {
return {
verifyOnline: verifyOnline(config),
verifyOffline: verifyOffline(config)
};
};
var _default = Keycloak;
exports["default"] = _default;

Wait for the promise

I'm trying to use the code from this answer to extend one OSS application.
However app.js is sync and no matter what I do, I cant force it to wait for the promise to resolve.
app.js
var cosmos = require('./cosmos.js');
const key = cosmos.key(var1, var2, var3);
console.log(key); // << shows Promise { <pending> }
mongoose.connect(`redacted`, {
auth: {
username: config.database.name,
password: key
}
});
cosmos.js
async function retriveKey(subId, resGrp, server) {
const { EnvironmentCredential } = require("#azure/identity");
const { CosmosDBManagementClient } = require("#azure/arm-cosmosdb");
const armClient = new CosmosDBManagementClient(
new EnvironmentCredential(), subId
);
const { primaryMasterKey } = await armClient.databaseAccounts.listKeys(
resGrp, server
);
return new Promise(resolve => {
setTimeout(() => resolve(primaryMasterKey), 1000);
});
}
exports.key = retriveKey
If i console.log() inside the async function it actually shows the key, however mongoose db connection doesn't wait for the promise to get resolved, it starts connecting straight away and fails with something like: password must be a string.
If i hardcode actual key instead of this promise - everything works fine.
EDIT:
halfway there:
// pull cosmos keys
async function retriveKey(subId, resGrp, server) {
const { EnvironmentCredential } = require("#azure/identity");
const { CosmosDBManagementClient } = require("#azure/arm-cosmosdb");
const armClient = new CosmosDBManagementClient(
new EnvironmentCredential(), subId
);
const { primaryMasterKey } = await armClient.databaseAccounts.listKeys(
resGrp, server
);
return primaryMasterKey // don't even need a promise here
}
exports.key = retriveKey
var mongooseConnected; // global variable
app.use(function (req, res, next) {
if (!moongooseConnected) {
moongooseConnected = cosmos.key(var1, var2, var3).then(function (key) {
mongoose.connect(`xxx`,
{
auth: {
username: config.database.name,
password: key
}
}
);
console.log(key); // works as expected
require('./models/user');
require('./models/audit');
require('./routes/user')(app);
require('./routes/audit')(app, io);
});
}
moongooseConnected.then(function () {
next();
});
});
the database connection gets established, console.log(key) shows proper key in the log, however no routes are present in the app.
if i move routes or models outside of this app.use(xyz) - i'm starting to see failures due to:
Connection 0 was disconnected when calling createCollection
or
MongooseError [MissingSchemaError]: Schema hasn't been registered for model "User".
which (i assume) means they require mongoose to be instantiated, but they are not waiting.
If you switch from CommonJS modules to ES modules, you can use await to wait for a promise to resolve:
import cosmos from './cosmos.js';
const key = await cosmos.key(var1, var2, var3);
console.log(key);
await mongoose.connect(`redacted`, {
auth: {
username: config.database.name,
password: key
}
});
Alternatively, you can wait with the initialization of mongoose until the first request comes in, because express middleware is asynchronous:
var mongooseConnected; // global variable
function connectMongoose() {
if (!mongooseConnected)
mongooseConnected = cosmos.key(var1, var2, var3)
.then(key => mongoose.connect(`redacted`, {
auth: {
username: config.database.name,
password: key
}
}));
return mongooseConnected;
}
module.exports = connectMongoose;
If the code above is needed elsewhere, it can be put in a separate module and imported wherever needed:
const connectMongoose = require("./connectMongoose");
app.use(function(req, res, next) {
connectMongoose().then(function() {
next();
});
});
require('./routes/user')(app);
require('./routes/audit')(app, io);
Note that if several parallel requests come in, only the first of these will let the global variable mongooseConnected equal a promise, and all these requests will wait for it to resolve before calling next().
Also note that additional routes of app must be registered after this app.use command, not inside it.
unless somebody comes up with a way to do this with less changes to the original code base, this is what I'm using:
cosmos.js
// pull cosmos keys
async function retriveKey(subId, resGrp, server) {
const { DefaultAzureCredential } = require("#azure/identity");
const { CosmosDBManagementClient } = require("#azure/arm-cosmosdb");
const armClient = new CosmosDBManagementClient(
new DefaultAzureCredential(), subId
);
const { primaryMasterKey } = await armClient.databaseAccounts.listKeys(
resGrp, server
);
return primaryMasterKey
}
exports.key = retriveKey
app.js
// pull cosmos keys
var cosmos = require('./cosmos');
let key = cosmos.key(var1, var2, var3)
mongoose.connect(
`xxx`,
{
auth: {
username: config.database.name,
password: key
}
}
).catch(
err => {
console.log("dOrty h4ck");
key.then(k => mongoose.connect(
`xxx`,
{
auth: {
username: config.database.name,
password: k
}
}
)
);
}
)
basically, like Heiko mentioned, mongoose.connect() is actually async, but somehow blocking (??). so while first mongoose.connect() always fails - it gives enough time for the code to retrieve the key, then I catch the error and connect again. no other changes to the original code base are needed.

how to pass proxy server in #aws-crypto/client-node node js encryption client

I am using #aws-crypto/client-node npm module to encrypt decrypt file using KMS key.
but when I run the following code.
I get error 'Missing credentials in config'
const {
KmsKeyringNode,
encrypt,
decrypt
} = require("#aws-crypto/client-node");
const encryptData = async (plainText, context) => {
try {
const {
result
} = await encrypt(keyring, plainText, {
encryptionContext: context
});
return result;
} catch (e) {
console.log(e);
}
};
encryptData('hello world', {
stage: "test",
purpose: "poc",
origin: "us-east-1"
})
I can see couple of issues with this code:
You are trying to import encrypt and decrypt functions directly from the module. This is not how aws-crypto works. You need to use build client to create instance which will hold these methods.
You are using keyring variable, but keyring is never declared? You need to create a keyring using .KmsKeyringNode method.
In order to properly use AWS/KMS to encrypt and decrypt data, take a look into the example bellow. (Make a note that this example does not use a context for its simplicity, nor additional keys which you can add. Also this example assumes that you are using same key for encryption and decryption as well)
const {
AMAZON_ENCRYPTION_KEY_ARN
} = process.env;
const awsCrypto = require('#aws-crypto/client-node');
const awsEncryptionClient = awsCrypto.buildClient(
awsCrypto.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
);
const keyring = new awsCrypto.KmsKeyringNode({
generatorKeyId: AMAZON_ENCRYPTION_KEY_ARN
});
const encrypt = async (data) => {
try {
const { result } = await awsEncryptionClient.encrypt(keyring, data);
return result.toString('base64');
}
catch(err) {
console.log('Encryption error: ', err);
throw err;
}
};
const decrypt = async (encryptedData) => {
try {
const encryptedBuffer = Buffer.from(encryptedData, 'base64');
const { plaintext } = await awsEncryptionClient.decrypt(keyring, encryptedBuffer);
return plaintext.toString('utf8');
}
catch(err) {
console.log('Decryption error: ', err);
throw err;
}
};
module.exports = {
encrypt,
decrypt
};
You can create a file using code above and invoke functions by importing this file somewhere else. You will need to add encryption/decryption key arn. Beside encryption and decryption, encoding and decoding to base64 is added, so final result is suitable for storage (database for example)
For additional code examples take a look here.

NPM Factory-Bot/Girl how to export a factory definition for use in my NodeJS specs?

JS newbie here. I am using Jasmine to test a NodeJS application which uses MongoDB and Mongoose, and I would like to replace my static test fixtures with dynamic factories. https://github.com/ratson/factory-bot looks good to me.
However, all of the examples are from a single file and don't demonstrate exporting/importing between files, so I don't understand what to modules.exports = in order to use a factory in my specs.
I'm also using ES5 if that matters.
My question is: how do I export this definition?
spec/factories/user.js
const factory = require('factory-bot').factory;
factory.setAdapter(new FactoryBot.MongooseAdapter());
const User = require('../models/user');
factory.define('user', User, {
username: 'Bob',
expired: false
});
factory.extend('user', 'expiredUser', {
expired: true
});
And then how do I use my export so that I can make sampleUsers?
spec/controllers/user.js
const reqs = require("../support/require")
describe("GET /users", () => {
describe("index", () => {
var data = {};
var sampleUsers = factory.createMany('user', 5);
beforeEach((done) => {
reqs.Request.get(/users", (error, response, body) => {
data.status = response.statusCode;
data.body = JSON.parse(body);
done();
});
});
it("returns a 200 response status", () => {
expect(data.status).toBe(200);
});
it("responds with the users collection", async () => {
expect(data.body.users).toBe(sampleUsers);
});
});
});
Thanks in advance for any advice.
You just need to require your factory definitions before using them.
Here's an example of what you could do:
spec/factories/user.js
const { factory } = require('factory-bot');
const User = require('../models/user');
factory.setAdapter(new FactoryBot.MongooseAdapter());
factory.define('user', User, {
username: 'Bob',
expired: false
});
factory.extend('user', 'expiredUser', {
expired: true
});
spec/factories/index.js:
const { factory } = require("factory-bot");
// Require factories to use with the exported object
require("./user.js");
module.exports = factory;
spec/controllers/user.js:
const factory = require("../../factories");
...
const sampleUsers = factory.createMany('user', 5);
The key difference between the example above and your sample code is the index.js file which requires factory-bot and all the factory definitions. By requiring the definitions, you will be able to use them.
If you require('factory-bot') directly instead of require('spec/factories'), you will need to require the factory definitions you want to use.

Resources