K6 trigger ASP.NET Core server function - .net-core

I have an ASP.NET Core Web API project that is using SignalR, I have a hub there which I am connecting to it using k6 (I want to do some load testings) I manage to connect to my hub but I can not figure out how to call a function from my server, my code is
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
var token = "Bearer userAccessToken";
const url = 'wss://localhost:5001/session';
const params = { headers: { "Authorization": token } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', () => {
console.log("opened");
socket.send(JSON.stringify({ UserId: "aUserId", GameId: "AGameId" }))
});
socket.on('close', () => console.log('disconnected'));
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
My function is called joinGameSession and it takes two variables the user id and the gameId
public async Task<bool> JoinGameSession(JoinGameRequest request)
{
return true;
}
I have managed to trigger functions using Microsoft's SignalR client.
const signalR = require("#microsoft/signalr");
require('dotenv').config();
var token = process.env.token ?? "";
var questionIndex = 0;
let connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:5000/session", { headers: { "Authorization": token } })
.withAutomaticReconnect()
.build();
connection.start().then(() => {
connection.invoke("JoinGameSession", { UserId: "a", GameId: "x" });
}).catch(e => {
console.log(e);
})
but I can not do it with k6, is there any other tools to achieve my goal?
Thank you.

Related

Telegram bot won't respond even doe webhook is set

The code for the bot is currently hosted on a Cloudflare worker, and there are no errors being reported from that end. Additionally, upon investigation of the Botfather side, everything seems to be functioning normally as well. However, despite attempting various solutions such as changing bots, tokens, and chat groups, the issue remains.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const { pathname } = new URL(request.url)
if (pathname === '/') {
return new Response('Hello! This is a Telegram bot. Send me a message.')
}
const { text } = await request.json()
if (text.startsWith('/start')) {
return new Response('Welcome to the bot! Use the /help command to see available options.')
} else if (text.startsWith('/help')) {
return new Response('Available commands:\n/scrape - scrapes videos from the specified website and sends them to the Telegram chat.\n/<example> - scrapes posts from the specified website and sends them to the Telegram chat.')
} else if (text.startsWith('/scrape')) {
const videoUrl = await scrapeVideoUrl('<example url>')
const message = `Here's the latest video: ${videoUrl}`
await sendMessageToChatId(message)
return new Response('OK')
} else if (text.startsWith('/<example>')) {
const post = await scrapePost('<example url>')
const message = `Here's the latest post: ${post.title}\n${post.url}`
await sendMessageToChatId(message)
return new Response('OK')
} else {
return new Response('Invalid command. Use the /help command to see available options.')
}
}
async function scrapeVideoUrl(url) {
const response = await fetch(url)
const html = await response.text()
const cheerio = require('cheerio')
const $ = cheerio.load(html)
const videoUrl = $('div.media > a').attr('href')
return videoUrl
}
async function scrapePost(url) {
const response = await fetch(url)
const html = await response.text()
const cheerio = require('cheerio')
const $ = cheerio.load(html)
const post = {
title: $('div.content h1').text(),
url: url
}
return post
}
async function sendMessageToChatId(message) {
const telegramApiUrl = "https://api.telegram.org/bot<token>/sendMessage";
const chatId = "<id>";
const response = await fetch(telegramApiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
chat_id: chatId,
text: message
})
})
}
There is zero response on the telegram side, even doe on web side everything seems to be fine.

Axios getStore is undefined in NextJs api calls. (Redux, NextJs, Jwt)

I am trying to set up authentication for a project. Once a user signs up for our app they get sent to our home page with an id in the query. This id then gets used to submit user and then the jwt token gets saved inside redux state.
All our calls now go through an axios client where the jwt token is passed on every request. The token gets read with store.getState(injectStore)
This all works fine inside getserversideProps, but the issue comes in when using calls on the frontend that goes through NextJs built in 'pages/api' folder. Any calls inside those folders causes the store.getState() to be undefined. I do not understand why since it uses the exact same client as geserversideProps.
Example GetServersideProps(working)
try {
const response = await serverApiClient.get('v1/config');
return {
props: {
},
};
} catch ({ error: { statusCode = 500, message = 'Internal Server Error' } }) {
if (statusCode === 401) {
return {
redirect: {
permanent: false,
destination: '/',
},
};
}
throw new Error(message as string);
}
};
Example Frontend bff call(not working)
try {
// Call below get sent to next built in api
const players = await apiClient.get(`/defenders?sortBy=${statId}&team_id=${teamShortName}`);
return players;
} catch (error) {
return { error };
}
};
export default async function handler(req: NextApiRequest) {
console.log('Start request')
try {
const { sortBy, team_id: teamId } = req.query;
const response = await serverApiClient.get(`/v1/players/picks?position=DEF&sort_by=${sortBy}&team_id=${teamId}`);
Api Client
mergeConfigs(
params: Record<string, string>,
headers: Record<string, string>,
configs: Record<string, string>,
): AxiosRequestConfig {
const defaultConfigs = ApiClient.getDefaultConfigs();
*const token = store?.getState()?.jwtToken?.value*
//ISSUE ABOVE - This store .getState() is only undefined in nextJS api folder calls.
return {
...defaultConfigs,
...configs,
params,
headers: {
...defaultConfigs.headers,
...headers,
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
};
}
get(
uri: string,
params = {},
headers = {},
configs = {},
): Promise<AxiosResponse | any> {
return this.client
.get(uri, this.mergeConfigs(params, headers, configs))
.then((response) => {
return (response.data ? response.data : response);
})
.catch((error) => {
const errorObject = {
error: error?.response?.data,
};
throw Object.assign(errorObject);
});
}
If anyone has some advice on why that getStore is undefined in frontend-to-backend calls please assist. Thanks all!

sending request via Fetch API from ASP.NET razor view

I'm trying to send a request via Feth
ch API to the remote api that serves over Http. I'm getting this error on my developer tools window.
Here is js.
(function () {
login();
})();
function login() {
document.getElementById("submitBtn").addEventListener("click", function () {
sendLoginAjax();
});
};
function sendLoginAjax(userName, password) {
var form = document.getElementById("loginForm");
var params = "UserName=" + userName + "&Password" + password + "&IPs=";
// Bind the FormData object and the form element
const FD = new FormData();
FD.append("func", "Login");
FD.append("params", params);
fetch("http://isapi.mekashron.com/soapclient/soapclient.php?URL=http://isapi.icu-tech.com/icutech-test.dll%2Fwsdl%2FIICUTech", {
method: 'POST',
body: FD
}).then(result => result.json()).then(
(result) => {
console.log(result);
}
);
}
I don't know what I should do to solve it.
Have any fix?

FCM very slow and unreliable when sending to a group of recipients through Cloud Function

I have the following Function that:
Listens for document (text message) creation
Grab IDs of members of a group chat
Get the FCM Tokens for each member
With a for-loop, send messages to group members
exports.sendChatMessage = functions.firestore
.document("chats/{mealID}/messages/{messageID}")
.onCreate((snap, context) => {
const data = snap.data();
const mealID = context.params.mealID;
const senderID = data.senderID;
const senderName = data.senderName;
const messageContent = data.content;
var docRef = db.collection("chats").doc(mealID);
docRef
.get()
.then((doc) => {
if (doc.exists) {
const docData = doc.data();
const mealName = docData.name;
const userStatus = docData.userStatus;
var users = docData.to;
var eligibleUsers = users.filter(
(user) => userStatus[user] == "accepted"
);
eligibleUsers.push(docData.from);
// get fcmTokens from eligibleUsers and send the messagme
db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {
var fcmTokens = [];
var thumbnailPicURL = "";
// get thumbnailpic of the sender and collect fcmTokens
snapshot.forEach((doc) => {
if (doc.data().uid == senderID) {
thumbnailPicURL =
doc.data().thumbnailPicURL == null
? "https://i.imgur.com/8wSudUk.png"
: doc.data().thumbnailPicURL;
} else {
fcmTokens.push(doc.data().fcmToken);
}
});
// send the message fcmTokens
fcmTokens.forEach((token) => {
if (token != "") {
const fcmMessage = {
message: {
token: token,
notification: {
title: mealName,
body: senderName + ": " + messageContent,
image: thumbnailPicURL,
},
apns: {
payload: {
aps: {
category: "MESSAGE_RECEIVED",
},
MEAL_ID: mealID,
},
},
},
};
tokenManger.sendFcmMessage(fcmMessage);
}
});
return true;
});
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
return false;
}
})
.catch((error) => {
console.log("Error getting document:", error);
return false;
});
return true;
});
My send function comes from a helper file that uses the HTTP V1 protocol to build the send-request:
const { google } = require("googleapis");
const https = require("https");
const MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
const SCOPES = [MESSAGING_SCOPE];
const PROJECT_ID = MY_PROJECT_ID;
const HOST = "fcm.googleapis.com";
const PATH = "/v1/projects/" + PROJECT_ID + "/messages:send";
exports.getAccessToken = () => {
return new Promise(function (resolve, reject) {
const key = require("./service-account.json");
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
};
//send message
exports.sendFcmMessage = (fcmMessage) => {
this.getAccessToken().then(function (accessToken) {
var options = {
hostname: HOST,
path: PATH,
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
},
// … plus the body of your notification or data message
};
var request = https.request(options, function (resp) {
resp.setEncoding("utf8");
resp.on("data", function (data) {
console.log("Message sent to Firebase for delivery, response:");
console.log(data);
});
});
request.on("error", function (err) {
console.log("Unable to send message to Firebase");
console.log(err);
});
request.write(JSON.stringify(fcmMessage));
request.end();
});
};
It worked all fine in the emulator but once deployed, there're significant delays (~3 mins):
I also noticed that the console says the cloud function finishes execution BEFORE sendFcmMessage logs success messages.
I did some research online, it appears that it might have something to do with the usage of Promise but I wasn't sure if that's the sole reason or it has something to do with my for-loop.
The Problem
To summarize the issue, you are creating "floating promises" or starting other asynchronous tasks (like in sendFcmMessage) where you aren't returning a promise because they use callbacks instead.
In a deployed function, as soon as the function returns its result or the Promise chain resolves, all further actions should be treated as if they will never be executed as documented here. An "inactive" function might be terminated at any time, is severely throttled and any network calls you make (like setting data in database or calling out to FCM) may never be executed.
An indicator that you haven't properly chained the promises is when you see the function completion log message ("Function execution took...") before other messages you are logging. When you see this, you need to look at the code you are running and confirm whether you have any "floating promises" or are using callback-based APIs. Once you have changed the callback-based APIs to use promises and then made sure they are all chained together properly, you should see a significant boost in performance.
The fixes
Sending the message data to FCM
In your tokenManger file, getAccessToken() could be reworked slightly and sendFcmMessage should be converted to return a Promise:
exports.getAccessToken = () => {
return new Promise(function (resolve, reject) {
const key = require("./service-account.json");
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(
(err, tokens) => err ? reject(err) : resolve(tokens.access_token)
);
});
};
//send message
exports.sendFcmMessage = (fcmMessage) => {
// CHANGED: return the Promise
return this.getAccessToken().then(function (accessToken) {
const options = {
hostname: HOST,
path: PATH,
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
},
// … plus the body of your notification or data message
};
// CHANGED: convert to Promise:
return new Promise((resolve, reject) => {
const request = https.request(options, (resp) => {
resp.setEncoding("utf8");
resp.on("data", resolve);
resp.on("error", reject);
});
request.on("error", reject);
request.write(JSON.stringify(fcmMessage));
request.end();
});
});
};
However, the above code was built for googleapis ^52.1.0 and google-auth-library ^6.0.3. The modern versions of these modules are v92.0.0 and v7.11.0 respectively. This means you should really update the code to use these later versions like so:
// Import JWT module directly
const { JWT } = require('google-auth-library');
// FIREBASE_CONFIG is a JSON string available in Cloud Functions
const PROJECT_ID = JSON.parse(process.env.FIREBASE_CONFIG).projectId;
const FCM_ENDPOINT = `https://fcm.googleapis.com/v1/projects/${PROJECT_ID}/messages:send`;
const FCM_SCOPES = ["https://www.googleapis.com/auth/firebase.messaging"];
exports.sendFcmMessage = (fcmMessage) => {
const key = require("./service-account.json"); // consider moving outside of function (so it throws an error during deployment if its missing)
const client = new JWT({
email: key.client_email,
key: key.private_key,
scopes: FCM_SCOPES
});
return client.request({ // <-- this uses `gaxios`, Google's fork of `axios` built for Promise-based APIs
url: FCM_ENDPOINT,
method: "POST",
data: fcmMessage
});
}
Better yet, just use the messaging APIs provided by the Firebase Admin SDKs that handle the details for you. Just feed it the message and tokens as needed.
import { initializeApp } from "firebase-admin/app";
import { getMessaging } from "firebase-admin/messaging";
initializeApp(); // initializes using default credentials provided by Cloud Functions
const fcm = getMessaging();
fcm.send(message) // send to one (uses the given token)
fcm.sendAll(messagesArr) // send to many at once (each message uses the given token)
fcm.sendMulticast(message) // send to many at once (uses a `tokens` array instead of `token`)
The Cloud Function
Updating the main Cloud Function, you'd get:
exports.sendChatMessage = functions.firestore
.document("chats/{mealID}/messages/{messageID}")
.onCreate((snap, context) => {
const mealID = context.params.mealID;
const { senderID, senderName, content: messageContent } = snap.data();
const docRef = db.collection("chats").doc(mealID);
/* --> */ return docRef
.get()
.then((doc) => {
if (!doc.exists) { // CHANGED: Fail fast and avoid else statements
console.log(`Could not find "chat:${mealID}"!`);
return false;
}
const { userStatus, to: users, name: mealName, from: fromUser } = doc.data();
const eligibleUsers = users.filter(
(user) => userStatus[user] == "accepted"
);
eligibleUsers.push(fromUser);
// get fcmTokens from eligibleUsers and send the message
/* --> */ return db.collection("users")
.where("uid", "in", eligibleUsers) // WARNING: This will only work for up to 10 users! You'll need to break it up into chunks of 10 if there are more.
.get()
.then(async (snapshot) => {
const fcmTokens = [];
let thumbnailPicURL = "";
// get thumbnailpic of the sender and collect fcmTokens
snapshot.forEach((doc) => {
if (doc.get("uid") == senderID) {
thumbnailPicURL = doc.get("thumbnailPicURL"); // update with given thumbnail pic
} else {
fcmTokens.push(doc.get("fcmToken"));
}
});
const baseMessage = {
notification: {
title: mealName,
body: senderName + ": " + messageContent,
image: thumbnailPicURL || "https://i.imgur.com/8wSudUk.png", // CHANGED: specified fallback image here
},
apns: {
payload: {
aps: {
category: "MESSAGE_RECEIVED",
},
MEAL_ID: mealID,
},
}
}
// log error if fcmTokens empty?
// ----- OPTION 1 -----
// send the message to each fcmToken
const messagePromises = fcmTokens.map((token) => {
if (!token) // handle "" and undefined
return; // skip
/* --> */ return tokenManger
.sendFcmMessage({
message: { ...baseMessage, token }
})
.catch((err) => { // catch the error here, so as many notifications are sent out as possible
console.error(`Failed to send message to "fcm:${token}"`, err);
})
});
await Promise.all(messagePromises); // wait for all messages to be sent out
// --------------------
// ----- OPTION 2 -----
// send the message to each fcmToken
await getMessaging().sendAll(
fcmTokens.map((token) => ({ ...baseMessage, token }))
);
// --------------------
return true;
})
.catch((error) => {
console.log("Error sending messages:", error);
return false;
});
})
.catch((error) => {
console.log("Error getting document:", error);
return false;
});
});
I found out that the culprit is my queries to db. Like #samthecodingman commented, I was creating floating Promises.
Originally, I have codes like:
db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {...}
All I needed to do is to return that call:
return db.collection("users")
.where("uid", "in", eligibleUsers)
.get()
.then((snapshot) => {...}
Although it's still not instant delivery, it's much faster now.

Firebase Callable function with JWT Authentication and Google Sheets V4 API

I want to implement firebase callable function with JWT Authentication and fetching data from Google Sheet, using Google Sheets V4 API.
For test I tried to use Example Spreadsheet but Sheets API not activated for that Spreadsheet and I cloned it on my own drive and use it for testing.
References:
My code based on solution described in this question How to use Google sheets API while inside a google cloud function and Accessing Google APIs using Service account in Node.JS
Also I have got two important information: "Service Account".json and API Key. I save API Key in api_key.json but didn't find examples how to use it with Google Sheets V4 API:
{
key: "xxxxxx"
}
test() callable function which doesn't require any authentication works fine:
exports.test = functions.https.onCall((data, context) => {
return { text: data.text };
});
Calling test() function somewhere on client (in Browser):
function getTest() {
console.log("clicked getTest()");
var test = firebase.functions().httpsCallable('test');
test({text: '12345'}).then(function(result) {
console.log(result);
}).catch(function(error) {
console.log(error.code);
console.log(error.message);
});
}
Calling getData() somewhere on client (in Browser):
function requestData() {
console.log("clicked requestData()");
//https://firebase.google.com/docs/functions/callable
//getData() function described in functions/index.js
var getData = firebase.functions().httpsCallable('getData');
getData(null).then(function (result) {
// Read result of the Cloud Function.
console.log(result); //<------- Expected rows from Spreadsheet????
}).catch(function(error) {
console.log(error.code);
console.log(error.message);
});
}
**Thank you, F10. I corrected code.
index.js:
'use strict'
const functions = require('firebase-functions');
const { google } = require('googleapis');
var serviceAccount = require("./credentials/owner-service-account-gcloud.json");
function getJwt() {
// Define the required scopes.
var scopes = [
'https://www.googleapis.com/auth/spreadsheets'
];
return new google.auth.JWT(
serviceAccount.client_email,
null,
serviceAccount.private_key,
scopes
);
}
function getSpreadsheetDate(jwt) {
return new Promise((resolve, reject) => {
jwt.authorize((error, access_token) => {
if (error) {
console.log('Error in jwt.authorize: ' + error);
reject(error);
} else {
// access_token ready to use to fetch data and return to client
const sheets = google.sheets({ version: 'v4', access_token });
// set auth as a global default:
google.options({ auth: jwt }); //<----------------------
const request = {
auth: jwt,
spreadsheetId: 'xxxx',
range: 'Class Data!A2:E', //'Class Data!A2:E',
}
sheets.spreadsheets.values.get(request, (err, response) => {
console.log("inside: sheets.spreadsheets.values.get() -------------------------------");
if (err) {
console.log('The Sheets API returned an error: ' + err);
//The API returned an error: Error: API key not valid. Please pass a valid API key.
reject(err);
};
try {
var numRows = response.data.values ? response.data.values.length : 0;
console.log('%d rows retrieved.', numRows);
console.log("response.data:-------------------------------");
console.log(response.data.values);
resolve(response.data.values);
} catch (err) {
console.log("Error processing Sheets API response: " + err);
reject(err);
}
})
}
})
})
}
exports.getData = functions.https.onCall((data, context) => {
console.log("getData()---------------------------");
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' + 'while authenticated.');
} else {
console.log("context.auth ------------ OK");
const uid = context.auth.uid;
console.log(uid);
var jwt = getJwt();
console.log("getJwt() --------------- OK");
return getSpreadsheetDate(jwt); //<------------ Requested Spreadsheet's Data
}
})
exports.test = functions.https.onCall((data, context) => {
return { text: data.text };
});
There's a solution that uses googleapis instead of the auth library to do the authentication with JWT. Regarding your token inquiries, you could check the OAuth 2.0 for client-side web applications documentations, which explains the steps to do the authentication.

Resources