Issue leveraging keycloak-verify to instantiate keyclock object verifier - node.js

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;

Related

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.

Unable to stub an exported function with Sinon

I need to test the following createFacebookAdVideoFromUrl() that consumes a retryAsyncCall that I'd like to stub with Sinon :
async function createFacebookAdVideoFromUrl(accountId, videoUrl, title, facebookToken = FACEBOOK_TOKEN, options = null, businessId = null) {
const method = 'POST';
const url = `${FACEBOOK_URL}${adsSdk.FacebookAdsApi.VERSION}/${accountId}/advideos`;
const formData = {
access_token: businessId ? getFacebookConfig(businessId).token : facebookToken,
title,
name: title,
file_url: videoUrl,
};
const callback = () => requestPromise({ method, url, formData });
const name = 'createFacebookAdVideoFromUrl';
const retryCallParameters = buildRetryCallParameters(name, options);
const adVideo = await retryAsyncCall(callback, retryCallParameters);
logger.info('ADVIDEO', adVideo);
return { id: JSON.parse(adVideo).id, title };
}
This retryAsyncCall function is exported as such:
module.exports.retryAsyncCall = async (callback, retryCallParameters, noRetryFor = [], customRetryCondition = null) => {
// Implementation details ...
}
Here is how I wrote my test so far:
it.only("should create the video calling business's Facebook ids", async () => {
const payload = createPayloadDataBuilder({
businessId: faker.internet.url(),
});
const retryAsyncCallStub = sinon.stub(retryAsyncCallModule, 'retryAsyncCall').resolves('random');
const createdFacebookAd = await FacebookGateway.createFacebookAdVideoFromUrl(
payload.accountId,
payload.videoUrl,
payload.title,
payload.facebookToken,
payload.options,
payload.businessId,
);
assert.strictEqual(retryAsyncCallStub.calledOnce, true);
assert.strictEqual(createdFacebookAd, { id: 'asdf', title: 'asdf' });
});
I don't expect it to work straightaway as I am working in TDD fashion, but I do expect the retryAsyncCall to be stubbed out. Yet, I am still having this TypeError: Cannot read property 'inc' of undefined error from mocha, which refers to an inner function of retryAsyncCall.
How can I make sinon stubbing work?
I fixed it by changing the way to import in my SUT :
// from
const { retryAsyncCall } = require('../../../helpers/retry-async');
// to
const retry = require('../../../helpers/retry-async');
and in my test file :
// from
import * as retryAsyncCallModule from '../../../src/common/helpers/retry-async';
// to
import retryAsyncCallModule from '../../../src/common/helpers/retry-async';
The use of destructuring seemed to make a copy instead of using the same reference, thus, the stub was not applied on the right reference.

NodeJS GC function cannot be initialized

Trying out my first NodeJS cloud function so far unsuccessfully despite working fine VS code. Getting following error
Function cannot be initialized. Error: function terminated.
Looking through the logs I see some potential issues
Detailed stack trace: ReferenceError: supabase_public_url is not defined
Provided module can't be loaded (doesn't specify)
Thoughts: Am I doing it wrong with the secret manager and using the pub/sub incorrect?
My Code index.js
import { createClient } from '#supabase/supabase-js'
import sgMail from "#sendgrid/mail"
import { SecretManagerServiceClient } from '#google-cloud/secret-manager'
//activate cloud secret manager
const client = new SecretManagerServiceClient()
const supabaseUrl = client.accessSecretVersion(supabase_public_url)
const supabaseKey = client.accessSecretVersion(supabase_service_key)
const sendgridKey = client.accessSecretVersion(sendgrid_service_key)
sgMail.setApiKey(sendgridKey)
const supabase = createClient(supabaseUrl, supabaseKey)
// get data for supabase where notifications coins are true
const supabaseNotifications = async() => {
let { data, error } = await supabase
.from('xxx')
.select('*, xxx!inner(coin, xx, combo_change, combo_signal, combo_prev_signal), xxx!inner(email)')
.eq('crypto_signals.combo_change', true)
if(error) {
console.error(error)
return
}
return data
}
//create an array of user emails from supabase data
const userEmail = (data) => {
try {
const emailList = []
for (let i of data) {
if (emailList.includes(i.profiles.email) != true) {
emailList.push(i.profiles.email)
} else {}
}
return emailList
}
catch(e) {
console.log(e)
}
}
// function to take email list and supabase data to generate emails to users
const sendEmail = (e, data ) => {
try {
for (let i of e) {
const signalList = []
for (let x of data) {
if(i == x.profiles.email) {
signalList.push(x)
} else {}
}
// create msg and send from my email to the user
const msg = {
to: i,
from:"xxxx",
subject: "Coin notification alert from CryptoOwl",
text: "One or more of you coins have a new signal",
html: signalList.toString()
}
sgMail.send(msg)
console.log(i)
}
}
catch(e) {
console.log(e)
}
}
// main function combines all 3 functions (supabase is await)
async function main(){
let supabaseData = await supabaseNotifications();
let supabaseEmails = userEmail(supabaseData);
let sendgridEmails = sendEmail(supabaseEmails, supabaseData);
}
exports.sendgridNotifications = (event, context) => {
main()
};
my package.json with type module to use import above
{
"type":"module",
"dependencies":{
"#sendgrid/mail":"^7.6.1",
"#supabase/supabase-js":"1.30.0",
"#google-cloud/secret-manager": "^3.11.0"
}
}
I'm not at all versed in Google Secret Manager but a rapid look at the Node.js library documentation shows (if I'm not mistaking) that accessSecretVersion() is an asynchronous method.
As a matter of facts, we find in the doc examples like the following one:
async function accessSecretVersion() {
const [version] = await client.accessSecretVersion({
name: name,
});
// Extract the payload as a string.
const payload = version.payload.data.toString();
// WARNING: Do not print the secret in a production environment - this
// snippet is showing how to access the secret material.
console.info(`Payload: ${payload}`);
}
See https://cloud.google.com/secret-manager/docs/samples/secretmanager-access-secret-version#secretmanager_access_secret_version-nodejs

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

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!

How to use MongoDB locally and directline-js for state management in Bot Framework using NodeJs and Mongoose?

I am maintaining the bot state in a local MongoDB storage. When I am trying to hand-off the conversation to an agent using directline-js, it shows an error of BotFrameworkAdapter.sendActivity(): Missing Conversation ID. The conversation ID is being saved in MongoDB
The issue is arising when I change the middle layer from Array to MongoDB. I have already successfully implemented the same bot-human hand-off using directline-js with an Array and the default Memory Storage.
MemoryStorage in BotFramework
const { BotFrameworkAdapter, MemoryStorage, ConversationState, UserState } = require('botbuilder')
const memoryStorage = new MemoryStorage();
conversationState = new ConversationState(memoryStorage);
userState = new UserState(memoryStorage);
Middle Layer for Hand-Off to Agent
case '#connect':
const user = await this.provider.connectToAgent(conversationReference);
if (user) {
await turnContext.sendActivity(`You are connected to
${ user.userReference.user.name }\n ${ JSON.stringify(user.messages) }`);
await this.adapter.continueConversation(user.userReference, async
(userContext) => {
await userContext.sendActivity('You are now connected to an agent!');
});
}
else {
await turnContext.sendActivity('There are no users in the Queue right now.');
}
The this.adapter.continueConversation throws the error when using MongoDB.
While using Array it works fine. The MongoDB and Array object are both similar in structure.
Since this works with MemoryStorage and not your MongoDB implementation, I'm guessing that there's something wrong with your MongoDB implementation. This answer will focus on that. If this isn't the case, please provide your MongoDb implementation and/or a link to your repo and I can work off that.
Mongoose is only necessary if you want to use custom models/types/interfaces. For storage that implements BotState, you just need to write a custom Storage adapter.
The basics of this are documented here. Although written for C#, you can still apply the concepts to Node.
1. Install mongodb
npm i -S mongodb
2. Create a MongoDbStorage class file
MongoDbStorage.js
var MongoClient = require('mongodb').MongoClient;
module.exports = class MongoDbStorage {
constructor(connectionUrl, db, collection) {
this.url = connectionUrl;
this.db = db;
this.collection = collection;
this.mongoOptions = {
useNewUrlParser: true,
useUnifiedTopology: true
};
}
async read(keys) {
const client = await this.getClient();
try {
var col = await this.getCollection(client);
const data = {};
await Promise.all(keys.map(async (key) => {
const doc = await col.findOne({ _id: key });
data[key] = doc ? doc.document : null;
}));
return data;
} finally {
client.close();
}
}
async write(changes) {
const client = await this.getClient();
try {
var col = await this.getCollection(client);
await Promise.all(Object.keys(changes).map((key) => {
const changesCopy = { ...changes[key] };
const documentChange = {
_id: key,
document: changesCopy
};
const eTag = changes[key].eTag;
if (!eTag || eTag === '*') {
col.updateOne({ _id: key }, { $set: { ...documentChange } }, { upsert: true });
} else if (eTag.length > 0) {
col.replaceOne({ _id: eTag }, documentChange);
} else {
throw new Error('eTag empty');
}
}));
} finally {
client.close();
}
}
async delete(keys) {
const client = await this.getClient();
try {
var col = await this.getCollection(client);
await Promise.all(Object.keys(keys).map((key) => {
col.deleteOne({ _id: key });
}));
} finally {
client.close();
}
}
async getClient() {
const client = await MongoClient.connect(this.url, this.mongoOptions)
.catch(err => { throw err; });
if (!client) throw new Error('Unable to create MongoDB client');
return client;
}
async getCollection(client) {
return client.db(this.db).collection(this.collection);
}
};
Note: I've only done a little testing on this--enough to get it to work great with the Multi-Turn-Prompt Sample. Use at your own risk and modify as necessary.
I based this off of a combination of these three storage implementations:
memoryStorage
blobStorage
cosmosDbStorage
3. Use it in your bot
index.js
const MongoDbStorage = require('./MongoDbStorage');
const mongoDbStorage = new MongoDbStorage('mongodb://localhost:27017/', 'testDatabase', 'testCollection');
const conversationState = new ConversationState(mongoDbStorage);
const userState = new UserState(mongoDbStorage);

Resources