Node gRPC: sending metadata from server to client without error - javascript

From the client side, it is easy to add metadata for the server:
const meta = new grpc.Metadata();
meta.add('xyz', 'okay');
stub.service.Rpc(request, meta, (err, response) => {
});
The above can be accessed on the server like this:
call.metadata.get('xyz');
Now, if we need to send metadata from the server to the client, we do this:
const err = { code, details };
const meta = new grpc.Metadata();
meta.add('...', '...');
callback(err, null, meta);
Note that we are passing error, and the actual response is null.
How do I pass a null error and a non-null response, along with metadata?
If I do the following, it does not seem to work as there is no way to access the metadata on the client without the error.
callback(null, r, meta);
// `r` is some response message
Does gRPC spec explicitly disallow sending metadata from server to client when there is no error?
Also, while we're at it, I'd like someone explain how do we send trailing vs initial metadata from server to client in Node.
Relevant links:
https://github.com/grpc/grpc-node
Can I send a custom Error message from server to client GRPC?
How to add metadata to nodejs grpc call
https://github.com/grpc/grpc/issues/9053
https://medium.com/compli-engineering/grpc-nodejs-using-jwt-authentication-b048fef6ecb2

ServerUnaryCall.sendMetadata(responseMetadata)
server:
const method = (call, cb) => {
// code
call.sendMetadata(metadata)
// code
}
client:
const call = client.method(params, cb)
call.on('metadata', (metadata) => {
// code
})

Looks like you can use such code:
client.someFunction().on('metadata', (meta) => { /* any code */ })
At least on v0.9.x you can see: https://github.com/grpc/grpc-node/blob/v1.9.x/packages/grpc-native-core/src/client.js#L562

Related

The skill is rejecting the request when executed with additional properties in the JSON request

I'm having problems when I deploy my alexa skill to google cloud functions.
Initially, I'm using the following code.
My skill.js file
//imports here...
Router.post("/:locale", async (req, res) => {
const requestEnvelope = JSON.stringify(req.body);
try {
await new SkillRequestSignatureVerifier().verify(requestEnvelope, req.headers);
await new TimestampVerifier().verify(requestEnvelope);
} catch (err) {
console.log(`Error with message: ${err.message}`);
console.log(`Error object: ${JSON.stringify(err)}`);
return res.status(400).send(err.message);
}
const responseASK = await skill.invoke(req.body);
return res.status(200).send(responseASK);
});
module.exports = Router;
In my server.js file
const skillRoute = require("./skill");
const express = require("express");
const server = express();
server.use("/", skillRoute);
exports.server = functions.https.onRequest(server);
Everything goes well with the deploy, including, I can invoke my alexa skill normally. But, when running the validations for distribution, I get the following problem:
The skill is rejecting the request when executed with additional properties in the JSON request.
When we invoke the skill with additional parameters, the skill is rejecting it when we expect this to be accepted. Future versions of the Alexa Skills Kit may add new properties to the JSON request and response formats, while maintaining backward compatibility for the existing properties. Your code must be resilient to these types of changes. For example, your code for de-serializing a JSON request must not break when it encounters a new, unknown property. Please ensure that your code can handle new attributes and does not break when it encounters new properties in the JSON request.
Documentation Help:
Request and response JSON reference: Click here
On my server console I get the following errors:
Error with message: request body and signature does not match
and:
Error object: {"name":"AskSdk.SkillRequestSignatureVerifier Error"}
It seems to me that it may be the result of some ask-sdk-core update, as I have other servers working with the same code.

GET/POST methods on Express

I am configuring a server using express.
My question has nothing to do with the project itself because it is running great.
I just have a minor doubt about why I have to use GET when for me it makes more sense to use POST.
So, for short I am configuring an API key on the server side and fetching it on the client side so I can use it.
This is the snippet on the server side:
const apiKey = process.env.API_KEY;
console.log(`Your API key is ${apiKey}`);
const dataObject ={};
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
app.get('/all', sendData = (req,res) => {
res.send(dataObject)
})
app.post('/addText', (req,res) => {
let newEntry = {
agreement = req.body.agreement,
subjectivity = req.body.subjectivity
}
dataObject = newEntry;
res.send(dataObject);
} )
And then on the client side I fetch on the '/api' path:
const getApiKey = async () => {
// Getting API key from server
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
}
Ok, that's working and everything, but my question is:
On the first GET on the server side, I understand that I am sending the API key to the '/api' path so that I can retrieve this key with fetch on the client side. But if I am sending the api key to this path, why am I using GET and not POST?
Sorry if it seems a stupid question but I am having a hard time understanding the GET method.
Thanks!
You are not sending any API key to the server. The server is sending the API key to the client as a response. The client uses a GET request to get the API key from /api. The names of the methods (GET, POST, PUT, DELETE, ...) are from the perspective of the client.
"And then on the client side I fetch on the '/api' path:" No. First the client sends the request with
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
This triggers the callback in
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
and the server sends the response.
This code returns the API key from the server. It does not send it from the client to the server.
app.get('/api', (req,res) => {
res.send({key: apiKey})
}
The
res.send()
Function is constructing the response returned by the server to the client.
Usually, you use the GET method when the client has to read some data from the server, in this case, you want to read the API_KEY defined in the server. GET has no body, but every request may be parametric by passing parameters in the query string.
On the other hand, when you have to update or create an entity on the server, that's when POST (or PUT) enters into action. Here you pass your data in the body of the request.

How to find the Error message in Error object send by NodeJS in Angular

The Nodejs functions return an error from try/catch scope, such as the one below if the user doesn't exist of if a database is not reachable:
router.delete('/delete/:email', async (req, res) => {
var email = req.params.email;
try {
let result = await User.remove({"email": email});
res.status(204).send(email);
} catch (err) {
res.status(400).send(err);
}
});
I can also return the Error from Nodejs server by myself:
return res.status(400).send(new Error(`The user with email ${email} doesn't exist.`));
The first problem is that I can't find the error message that is embedded somewhere deep in the body the returned Error object. It is stored in one of its 100+ attributes. Where should I look for it so I could display in on a screen for the end user to read it?
Then, the err object generated by the try/catch scope has a set of different attributes comparing to the Error object created with new Error("Here is my error message"). Is there a way to normalize the returned Errors so they all have the same or similar attributes?
You don't need to return the whole error object from the server, and arguably shouldn't since error messages can expose internals about your code and infrastructure.
One way you could handle this is to format and return an error message from the server yourself. Assuming you're using express this would look something like:
return res.status(400).json({ message: `The user with email ${email} doesn't exist.` });
Alternatively you could use an error handling middleware like strong-error-handler found here: https://github.com/strongloop/strong-error-handler which automatically builds a json formatted message that's easier to parse, but keep in mind that the content of the message differs depending on whether you set debug mode to true or no.
If you want to develop a secure web application with nice error handling, i will suggest you the following structure.
Step 1. At front end divide your api calls in four main operations for e.g. inset,update,query and filter.
now whenever your page loads and you want to show some data fetched from server then your api call must be like 'https://domainname.tld/server/query' and send some payload with this api call according to need of your data requirements to be fetched.
At backend probably at Server.js handle like this :
app.all("/server/query", function (req, res) {
try {
console.log(a);
// some database or io blocking process
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
function writeCustomError(message) {
var errorObject = {};
errorObject.message = message;
errorObject.code = 10001; // as you want
errorObject.status = "failed";
return errorObject;
}
in try block you can also handle logical errors using same function i.e writeCustomError
So if you use this approach you can also implement end-to-end encryption and send only eP('encrypted payload') and eK('encryption Key'),by doing this end users and bad end users even can not evaluate your serve API calls.
If you are thinking how will you route different paths at server then simplest solution is send uri in payload from client to server for e.g
User wants to reset password :-
then
call api like this
https://domain.tld/server/execute and send Json object in payload like this {uri:"reset-password",old:"",new:""}.
at backend
use
app.all("/server/execute", function (req, res) {
try {
// decrypt payload
req.url = payload.uri;
next();
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
app.all("/reset-password", function (req, res) {
try {
// reset logic
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
so in this way only developer know where password reset logic and how it can called and what parameters are required.
I will also suggest you to create different router files for express like QueryRouter,InsertRouter etc.
Also try to implement end-to-end encryption.Any query regarding post,kindly comment it.

Parse raw body on cloudflare-worker service(Non NODE)

I've created an api server'ish environment on cloudflare using cloudflare-worker and there is no node server running(cloudflare-worker is pretty much serverless event handler service). It does provide the configurations to handle any subdomain calls much like how api works. I've used a package called cf-worker-router to do so.
My cloud service looks like this:
import { ApiError, ApiRedirect, DomainRouter, FetchRouter } from 'cf-worker-router';
const router = new FetchRouter();
// add the cloudflare event listener
addEventListener('fetch', (event) => {
router.onFetch(event);
});
router.route('/users', 'POST', async (event) => {
// automatically converts anything not of Response type to ApiResponse
return await event.request.text();
});
And what I did was create a POST request to the url and supplied some body to the request. I was able to get the request text successfully but now I can't figure out how to parse the text I received.
When using the request as multipart/form-data request and the received body text is as follows:
"----------------------------093590450792211419875705\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nJon Doe\r\n----------------------------093590450792211419875705--\r\n"
I tried sending application/x-www-form-urlencoded and I the response text as such:
"name=Jon%20Doe"
And Similar for application/json request:
"{\n\t\"name\": \"Jon Doe\"\n}"
Since cloudflare is not using nodejs server, body-parser can't be applied here. This service is pretty much an open api so it needs to take care of all sorts of request content types. Is there any way to identify and decode the strignified contents from any of these content types to a valid object in javascript?
To handle form data uploads, you can use the request.formData() method which will return a promise of a FormData object.
For example:
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const formData = await request.formData();
const name = formData.get('name');
return new Response(`Hello ${name}`);
}

Dialogflow Webhook (Webhook call failed. Error: 500 Internal Server Error)

I've followed this tutorial's code (https://dialogflow.com/docs/getting-started/basic-fulfillment-conversation) to return results of an API to dialog flow. However my webhook keeps failing. Can someone help me figure out why?
Here's one of the failed conversations:
Here's my code:
'use strict';
const http = require('http');
exports.Hadoop = (req, res) => {
// Get name node server from the request
let nameNodeServer = req.body.queryResult.parameters['nameNodeServer']; // nameNodeServer is a required param
// Call the Hadoop API
getNameNodeInfo(nameNodeServer).then(function(output) {
res.json({ 'fulfillmentText': output }); // Return the results to Dialogflow
}).catch(() => {
res.json({ 'fulfillmentText': 'getNameNodeInfo() Error'- });
});
};
function getNameNodeInfo (nameNodeServer) {
return new Promise((resolve, reject) => {
// Create url for the HTTP request to get the name node info
let url = 'http://' + nameNodeServer + '[rest of url]';
// Make the HTTP request to get the name node info
http.get(url, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (chunk) => {body += chunk; });
res.on('end', () => {
// After all the data has been received, parse the JSON for desired data
let response = JSON.parse(body);
let beans = response['beans'][0];
// Create response
let output = `Percent Used: ${beans['PercentUsed']}`;
// Resolve the promise with the output text
console.log(output);
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the Hadoop API: ${error}`);
reject();
});
});
});
}
I believe the getNameNodeInfo function and the retrieval of the name node server are correct, as they logged the correct output in debugging.
Diagnostic Info:
I contacted someone at Dialogflow and this was their response.
Thank you for providing all the information. I have observed in your
code that you have used http requests instead of https. The service
must use HTTPS and the URL must be publicly accessible in order for
the fulfillment to function. Dialogflow does not support self-signed
SSL certs. For information on SSL setup, please refer to this :
https://developers.google.com/web/fundamentals/security/encrypt-in-transit/enable-https
We've had a somewhat different, but related, issue:
Internal Server Error when running an agent.
“status”: {
“code”: 500,
“errorType”: “internal_server_error”,
“errorDetails”: “Internal Server Error”
},
This error was not caused by any changes we introduced. We are using that agent in a dev version of an app and one morning it stopped working.
We tested by creating a .zip and restoring into a new agent. The new agent would work properly, but we would continue to get the 500 error on the agent hooked into our dev app. We submitted a help request and overnight the error got resolved. We suspect that DialogFlow team had to manually reboot the server or something similar.

Categories

Resources