I was just trying out grpc. I made a simple client and server both with nodejs
The proto file:
syntax = "proto3";
message Empty {}
message EchoRequest {
string message = 1;
}
message EchoResponse {
string message = 1;
int32 message_count = 2;
}
// A simple echo service.
service EchoService {
// One request followed by one response
// The server returns the client message as-is.
rpc echo(EchoRequest) returns (EchoResponse);
}
client.js
const grpc = require("#grpc/grpc-js");
var protoLoader = require("#grpc/proto-loader");
const PROTO_PATH = "./echo.proto";
const options = {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
};
var packageDefinition = protoLoader.loadSync(PROTO_PATH, options);
const EchoService = grpc.loadPackageDefinition(packageDefinition).EchoService;
const client = new EchoService(
"165.22.201.129:9090",
grpc.credentials.createInsecure()
);
client.echo({message: "test"}, (error, response) => {
console.log("Error:", error)
console.log("Response message", response.message);
});
server.js
const grpc = require("#grpc/grpc-js");
const PROTO_PATH = "./echo.proto";
var protoLoader = require("#grpc/proto-loader");
const options = {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
};
var messageCount = 0;
var packageDefinition = protoLoader.loadSync(PROTO_PATH, options);
const echoProto = grpc.loadPackageDefinition(packageDefinition);
const server = new grpc.Server();
server.addService(echoProto.EchoService.service, {
echo: (call, callback) => {
console.log("request message:", call.request.message, "index", messageCount)
callback(null, {message: "response test"});//call.request.message);
},
});
const ip = "0.0.0.0:9090";
server.bindAsync(
ip,
grpc.ServerCredentials.createInsecure(),
(error, port) => {
console.log("error is:", error)
console.log("Server running at", ip);
server.start();
}
);
I run both the client and the server normally, without using anything like a docker container.
When running locally, everything works creat. However, when I run the server in a digitalocean droplet with the ip: 165.22.201.129. It doesn't work.
The server does receive the request without issues, however when using the callback I get the following error on the client:
Error: Error: 13 INTERNAL: Received RST_STREAM with code 0
at Object.callErrorFromStatus (D:\Github\EpicFlowServer\node_modules\#grpc\grpc-js\build\src\call.js:31:26) at Object.onReceiveStatus (D:\Github\EpicFlowServer\node_modules\#grpc\grpc-js\build\src\client.js:189:52)
at Object.onReceiveStatus (D:\Github\EpicFlowServer\node_modules\#grpc\grpc-js\build\src\client-interceptors.js:365:141)
at Object.onReceiveStatus (D:\Github\EpicFlowServer\node_modules\#grpc\grpc-js\build\src\client-interceptors.js:328:181)
at D:\Github\EpicFlowServer\node_modules\#grpc\grpc-js\build\src\call-stream.js:187:78
at processTicksAndRejections (internal/process/task_queues.js:79:11) {
code: 13,
details: 'Received RST_STREAM with code 0',
metadata: Metadata { internalRepr: Map {}, options: {} }
}
I tried looking online but couldn't find any help.
Does anyone have an idea of what could be going wrong?
Related
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!
Such as described here, I'm using local emulator (on-line) to make tests im my cloud functions.
Index.js:
var status = 200;
exports.saveAndSendMail = functions.https.onCall( async (req, res) => {
try{
let jsons = req.body;
await saveInfirestore(jsons);
await sendMail("Data saved", jsons);
} finally {
closeConnection(res, status);
}
async function saveInfirestore(json) {
//execute business logic and save in firestore (irrelevant for this question)
}
function closeConnection (res, status){
res.sendStatus(status);
res.end();
}
async function sendMail(title, message) {
try {
AWS.config.loadFromPath('./config_mail.json');
// Create sendEmail params
var params = {
Destination: {
ToAddresses: [
'mymail#gmail.com'
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: JSON.stringfy(message);
}
},
Subject: {
Charset: 'UTF-8',
Data: title
}
},
Source: '"Origin" <origin#gmail.com>',
ReplyToAddresses: [
'origin#gmail.com'
]
};
// Create the promise and SES service object
var sendPromise = new AWS.SES({apiVersion: '2022-17-01'}).sendEmail(params).promise();
}
catch(e){
throw e;
}
// Handle promise's fulfilled/rejected states
sendPromise.then(
function(data) {
console.log(data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});
}
index.test.js
const { expect } = require("chai");
const admin = require("firebase-admin");
const test = require("firebase-functions-test")({
projectId: process.env.GCLOUD_PROJECT,
});
const myFunctions = require("../index");
describe("Unit tests", () => {
after(() => {
test.cleanup();
});
it("test if save is correct", async () => {
const wrapped = test.wrap(myFunctions.saveAndSendMail);
const req = {
body: [{
value: 5,
name: 'mario'
}]
};
const result = await wrapped(req);
let snap = await db.collection("collection_data").get();
expect(snap.size).to.eq(1);
snap.forEach(doc => {
let data = doc.data();
expect(data.value).to.eql(5);
expect(data.name).to.eql('mario');
});
});
I execute it with: firebase emulators:exec "npm run test"
I have 2 problems.
1 - When execute, I receive the error TypeError: res.sendStatus is not a function. If I comment closeConnection call in block finally (index.js), this code run perfectly and all tests and "expect" run with success. But, this correct way is mock this method or mock 'res' calls. I tried mock with something like this:
const res = {
sendStatus: (status) => {
},
end: () => {
}
}
const result = await wrapped(req, res);
But, I receive this error:
Error: Options object {} has invalid key "sendStatus"
at /home/linuxuser/my-project/firebase/functions/myfolder/node_modules/firebase-functions-test/lib/main.js:99:19
at Array.forEach (<anonymous>)
at _checkOptionValidity (node_modules/firebase-functions-test/lib/main.js:97:26)
at wrapped (node_modules/firebase-functions-test/lib/main.js:57:13)
at Context.<anonymous> (test/index.test.js:50:26)
at processImmediate (node:internal/timers:464:21)
Problem 2:
I'm not wish receive an e-mail each time that tests executes. How I mock sendMail function?
Something very important to point out is that you are currently trying to use a Firebase callable function, as shown by the function heading functions.https.onCall(() => {});. Since you want to work with requests and response codes, the correct type of function to use is an HTTP function. You would only need to change the heading in your index.js:
exports.saveAndSendMail = functions.https.onRequest(async (req, res) => {
// function body
});
Now, your first problem can then be solved by correctly mocking the res object that is passed to the function (inside index.test.js). When testing HTTP functions, you must not use test.wrap() when calling the function, nor expect the result as you were doing with const result = await wrapped(req); This is since Wrap being only supported for testing onCall functions. You can see another snippet of how to call an HTTP function for testing in the documentation.
it("test if save is correct", async () => {
const req = {
body: [{
value: 5,
name: 'mario'
}]
};
// mocking the response object that is returned from the function:
const res = {
sendStatus: (code) => {
expect(code).to.eql(200); // asserting that we get 200 back as the response code
},
end: () => {
}
};
const result = await myFunctions.saveAndSendMail(req, res); // mocking a call to an HTTP function, without test.wrap()
// rest of the function…
For your second problem, I haven’t used AWS SES before, but it seems this library offers ways to mock the functions so that you won’t have to actually send emails during your tests.
I want to recover my collection which is in the database.
The problem is that my query is still not defined.
Here is my request:
const products = {
getProducts: async (req, res) => {
const productId = req.query.id;
console.log(productId);
const product = await ProductsModel.find({}).exec();
if (product instanceof Error) {
res.status(500).json({ message: "Error" });
return;
}
res.json(product);
},
};
module.exports = products;
My schema with mongoose :
const ProductSchema = new mongoose.Schema({
image: {
type: String,
},
title: {
type: String,
},
description: {
type: String,
},
price: {
type: Number,
},
category: {
type: String,
},
});
const Products = mongoose.model("Products", ProductSchema);
module.exports = Products;
My Fetch :
const [products, setProducts] = useState([]);
useEffect(getProducts, []);
async function getProducts() {
const options = {
method: "GET",
}
const response = await fetch("http://localhost:8000/products", options);
const productsData = await response.json();
setProducts(productsData);
console.log(productsData);
}
My error message :
[nodemon] restarting due to changes...
[nodemon] starting node ./bin/www
(node:4279) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.
(Use node --trace-deprecation ... to show where the warning was created)
undefined
GET /products 304 14.493 ms - -
Thank you in advance for your help.
At first, while exporting from your Schema file you have done:
module.exports = Products;
But on your Request file while fetching product you have done:
const product = await ProductsModel.find({}).exec();
You should replaced it with:
const product = await Products.find({}).exec();
You are getting undefined on req.query because while request on server you have not passed query. To pass query use ?id=
In your Fetch File you should do following change:
const response = await fetch("http://localhost:8000/products?id="+productId, options);
Or, You can also use Tempelate Literals ``, this is a good practice.
const response = await fetch(`http://localhost:8000/products?id=${productId}`, options);
For deprecation warning, you have to do following changes where you have connect databse.
mongoose.connect('mongodb://localhost:27017/name',{
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex:true},()=>{console.log('database connected')}
I have use Cloud function connect with onesignal service which will send notification to user. After test function locally, it's work perfectly but after deploying to the cloud function it return me an error "RequestError: Error: read ECONNRESET" which I thick the cloud function reset the connection
Here is what my code look like
exports.sendNotification = functions
.pubsub
.topic('cron-notification')
.onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})
}
});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})
}
Can anyone give suggestion for me?
According to the Node.js documentation ‘ECONNRESET’ error occurs when a connection is forcibly closed by peer. This results in loss of connection on the remote socket due to timeout or reboot. Since you mentioned the code is working locally and the error occurs after it is deployed, here is an answer that says the possible solution is to increase the number of cores so the requests can be serviced faster. Also, it might be useful to read the GitHub discussion on socket hang up error.
Just for people who face the same problem with me, I just notice that I did not return anything in the function. The cloud function will reset the function if it did not return any thing back. So from the code above should be like this and everything work fine.
exports.sendNotification = functions.pubsub.topic('cron-notification').onPublish(async (message) => {
const databaseRef = admin.database();
// Query all user from realtime db
const snapshotsUser = await databaseRef
.ref(`user`)
.orderByKey()
.once("value");
// Check if user exist
if (snapshotsUser) {
//Get the user key
const user_object_key = Object.keys(snapshotsUser.val());
// send notification for each user
return Promise.all([ user_object_key.map(async (user_id) => {
// query something
const snapshotsUser = await databaseRef
.ref(`record/user_id`)
.orderByKey()
.once("value");
const message = {
"app_id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"filters": [
{"field": "tag", "key": "user_id", "value": user_id}
],
"headings": {"en": `Hello World`},
"contents": {"en": `Hello`}
}
sendNotification(message);
})])}});
function sendNotification(message) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
// Use onesignal for send notification
const options = {
uri: "https://onesignal.com/api/v1/notifications",
headers: headers,
method: 'POST',
json: true,
body: message,
resolveWithFullResponse: true,
}
return request(options).then(response => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
} else {
console.log(response.body)
}
}).catch(error => {
console.log(error);
})}
I have an Angular 2 app which runs on port 8000 on the lite server. My express app (back-end) runs on port 3000.
When I send my credentials to the back-end which runs on port 3000, then I can see that I had succesfully logged in on port 8000. But I don't get a session as a response from the back-end. Here is a screenshot of it link:
As you can see the response says Session not found.
And when I succesfully log in on port 3000. Then I do get a session as response from the back-end. Here is a screenshot of it link:
Also here is a screenshot of logging in on port 3000 through Postman (Rest client) link:
And here is a screenshot of getting a session as a response on port 3000 link:
So now I can conclude that there is no problem at the back-end.
My question is: how can I also get the session as a response on port 8000?
Here is my code:
app.component.ts:
export class AppComponent {
//On page refresh the constructor gets called
constructor(private _userService: UserService) {
this._userService.getSession()
.subscribe(
res => {
console.log("app session ");
//log the response which shows a session
console.log(res);
},
error => {
console.log("app session error");
console.log(error);
}
);
}
}
login.component.ts:
export class LoginComponent {
user: User = new User();
loginRes: String;
private toasterService: ToasterService;
public toasterconfig: ToasterConfig = new ToasterConfig({
showCloseButton: true,
tapToDismiss: false,
timeout: 0
});
constructor(private _userService: UserService, toasterService: ToasterService, private router: Router) {
this.toasterService = toasterService;
}
data = {};
onSubmit() {
this._userService.login(this.user)
.subscribe(
//we are using an arrow function here
res => {
this.toasterService.pop('success', 'Success', 'You have logged in ' + res.username);
// I don't use navigate(), because I want a redirect where the page gets refreshed
// this.router.navigate(['/']);
var host = location.host;
console.log("host");
console.log(host);
//redirects where the page also gets refreshed
window.location.href = "http://"+ host;
},
error => {
this.toasterService.pop('error', 'Failed', error._body);
}
);
}
}
user.service.ts:
#Injectable()
export class UserService {
private url: string;
constructor(private _http: Http) {
}
login(user: User) {
this.url = config.getEnvironmentVariable('endPoint')+'api/auth/login';
let data = { "username": user.username, "password": user.password };
let body = JSON.stringify(data);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
console.log("login url " + this.url);
return this._http.post(this.url, body, options)
.map(this.extractData)
.catch(this.handleError);
}
getSession() {
this.url = config.getEnvironmentVariable('endPoint')+'api/auth/';
console.log("session url " + this.url);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
options.body = '';
return this._http.get(this.url, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: any) {
//the Observable catches and throws an error
return Observable.throw(error.message || error);
}
}