I've got an application built with Actions Builder with webhook handlers hosted on the firebase side.
As an application has been migrated from old bundle AOG + Dialogflow, fulfillment webhook was exposed in the next way:
const functions = require('firebase-functions');
const passport = require('passport');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fulfillment = require('../fulfillment');
app.use(bodyParser.json());
app.post('/',
async function(request, response) {
await fulfillment(request, response);
});
exports.fulfillment = functions.https.onRequest(app);
Everything is working fine for both local and released production version, however, when Google team tries to reach a webhook endpoint, it throws a 500 error with a message: Handler not found for handle name. Full trace of an error is:
Error: Handler not found for handle name:
at Function.handler (/workspace/node_modules/#assistant/conversation/dist/conversation/conversation.js:139:23)
at standard (/workspace/node_modules/#assistant/conversation/dist/assistant.js:50:32)
at /workspace/node_modules/#assistant/conversation/dist/framework/express.js:29:13
at omni (/workspace/node_modules/#assistant/conversation/dist/assistant.js:39:53)
at /workspace/src/functions/fulfillment.function.js:29:13
at Layer.handle [as handle_request] (/workspace/node_modules/express/lib/router/layer.js:95:5)
at next (/workspace/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/workspace/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/workspace/node_modules/express/lib/router/layer.js:95:5)
at /workspace/node_modules/express/lib/router/index.js:281:22
Line 29 is await fulfillment, which is declared above, so it doesn't make a lot of sense.
Have anybody else also experienced the same error, and how it can be solved? Any help appreciated.
It's hard to say for sure since you didn't post the contents of fulfillment but it also seems like 'handle name' is not defined. In Actions Builder, you have intents that are technically separate from the expected name that you'll receive in the webhook.
In the screenshot below, you specify "handler_name" as the handler you'll need in the webhook fulfillment.
Related
I use the latest version of sign_in_with_apple using the following code to allow signing in with Apple to my Flutter app on Android.
final credential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
webAuthenticationOptions: WebAuthenticationOptions(
clientId: '***Service Identifier***',
redirectUri:
// For web your redirect URI needs to be the host of the "current page",
// while for Android you will be using the API server that redirects back into your app via a deep link
kIsWeb ? Uri.parse('https://${window.location.host}/') : Uri.parse('https://***Backend***/callback'),
),
nonce: nonce,
);
I have taken the code for the backend from the package README.md:
apple_router.post("/callback", (request, response) => {
console.log(">>> Apple callback received <<<");
console.log("Body:");
console.log(request.body);
console.log("Query:");
console.log(request.query);
console.log("Params:");
console.log(request.params);
const redirect = `intent://callback?${new URLSearchParams(
request.body
).toString()}#Intent;package=${process.env.ANDROID_PACKAGE_IDENTIFIER
};scheme=signinwithapple;end`;
console.log(`Redirecting to ${redirect}`);
response.redirect(307, redirect);
});
I have also configured everything at Apple with the correct domains, but on my backend, when I log into the app, only an empty request arrives:
>>> Apple callback received <<<
Body:
{}
Query:
{}
Params:
{}
Redirecting to intent://callback?#Intent;package=***Android package ID***;scheme=signinwithapple;end
Which is why it doesn't work properly in the app either:
E/flutter (27962): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: SignInWithAppleAuthorizationError(AuthorizationErrorCode.invalidResponse, parseAuthorizationCredentialAppleIDFromDeeplink: No `code` query parameter set))
I have checked everything several times and I no longer have any idea where this problem could come from. Does anyone have any ideas?
Using this sign_in_with_apple apple will provide you the email, fullName, etc. information only for the first authorizations.
So, you have to take few steps to apple authorization.
Flutter/client side code/mechanism
void loginSignUpWithApple() async {
SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName
],
).then((value) {
_socialLoginSignUp(value.authorizationCode, describeEnum(LoginMethod.APPLE));
}).catchError((e) {
showMessage("Something went wrong: $e", isError: true);
});
}
Backend side code/mechanism
Generate JWT using apple key id and private key
Get auth token from apple server by using generated jwt and clint sent authorizationCode then extract the IdToken from the auth token response
Decode the IdToken and get the IdTokenHeader
From the IdTokenHeader get the kId
Using the kId get the list of keys from apple server then extract the actual key
Using the actual key create apple key to public key
Using public key and IdToken get the claims then decode the claim to get the user email
After getting the user email you can store this email in your database or other place and you can trust this is mail as a valid apple mail.
You can see the backend implementation code which is we have implemented previously in java (spring boot).
Sample backend code for apple login (Try with VPN if link is not working)
I found the problem: if you are using express as your server, add
bodyParser.urlencoded({ extended: true })
as middleware for your callback handler and then request.body will not be empty.
I have spent some hours trying to figure out my problem without any success.
before reading the explanation
My nuxt site generates dynamic content and works well on client side but for SEO to work and social media shares render dynamic content i need to move my app to SSR. This axios post request do work on client side rendering but does not on SSR and I don't understand the reason and I need help to understand it.
Thanks
For starters I am building a Nuxt app that consumes Drupal as a CMS using a fully decoupled approach and I have been using it for several VUE apps without problem and now I need to do them on Nuxt with a SSR approach because I need heavy SEO on the sites. With Nuxt the same request against oauth/token on drupal doesn't work and I have gone from the complex structure we had to the simple one using both Axios and Nuxt/Axios without any success and alway getting a 400 error code.
I need to run this code first on my app so I can get drupal access token and do some request for data.
Code on store
export const actions = {
async nuxtServerInit({dispatch}, vuexContext, context, app){
await dispatch("setToken");
},
async setToken({ commit, dispatch, getters }, context) {
//prep form data to send
const FormData = require('form-data');
const body = new FormData();
body.append("param1", apiConfig.getValue("param1"));
body.append("param2", apiConfig.getValue("param2"));
body.append("param3", apiConfig.getValue("param3"));
body.append("param4", apiConfig.getValue("param4"));
body.append("param5", apiConfig.getValue("5"));
await this.$axios.$post(url, body)
.then(({data}) => {
//code to process data
commit("SET_TOKEN_DATA", data);
}).catch(error => {
console.log(error)
}).finally(() => {
console.log("FINALY");
});;
}
Some updates
base url is defined on nuxt.config as
// module options for axios
axios: {
baseURL: 'mysite.com'
},
I have tied on http request the following request using both axios and nuxt/axios, I will write short request just to show what I edid
await this.$axios.$post('mysite.com/oauth/token', body)
await this.$axios.$post('/oauth/token', body)
await this.$axios.$post('oauth/token', body)
I have also tried to use
const api = $axios.crate({
baseURL: 'url'
})
api.$axios.post('oauth/token', body)
another updated
Crated a Client Side Nuxt app and the request works.
Fix it, after literally 16 hours trying. When multiform post data you need to send FormData headers on the post and again, this works without any issues using CSR on VUE and Nuxt.
await this.$axios.$post(url, body, { headers: body.getHeaders()}).yada
I don't know if it's Axios or Nuxt the one doing this but I followed a breadcrumb trail to this post https://github.com/axios/axios/issues/318
I want to use Google Tag Manager to send data to our Salesforce org for certain events on our website (user signup, conversion etc). After some research, I realized JSforce would be the easiest way to achieve this. I created a new connected app in Salesforce, tried out the Salesforce API using Postman and successfully managed to create a new user account via the API. Then I moved on to try and achieve the same thing in Google Tag Manager. I read JSforce's docs and attempted to implement everything. But, after multiple hours of troubleshooting and Google searching, I can't seem to make it work.
Here is my current code, which is in a 'tag' in Google Tag Manager that triggers on all pages (just for testing):
https://jsforce.github.io/start/#web-browser
<script src="//cdnjs.cloudflare.com/ajax/libs/jsforce/1.9.1/jsforce.min.js"></script>
<script>
jsforce.browser.init({
clientId: '<MYCLIENTID>',
redirectUri: 'https://cuttersclub.com'
});
https://jsforce.github.io/document/#access-token
var jsforce = require('jsforce');
var conn = new jsforce.Connection({
instanceUrl : 'https://um5.salesforce.com',
accessToken : '<MYACCESSTOKEN>',
});
https://jsforce.github.io/document/#create
conn.sobject("Account").create({ Name : 'My Account #1' }, function(err, ret) {
if (err || !ret.success) { return console.error(err, ret); }
console.log("Created record id : " + ret.id);
});
</script>
I'm getting this error in the browser console:
Uncaught ReferenceError: require is not defined
EDIT: Removing var jsforce = require('jsforce'); solved this problem and accounts are being created in Salesforce. But, now I am getting the following error in the browser console:
Access to XMLHttpRequest at '<URL>' from origin '<CALLBACKURL>' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
As mentioned in the JSforce docs, I think it may be something to do with proxy servers: https://github.com/jsforce/jsforce-ajax-proxy
I don't know that much about salesforce, but "require" is something from node.js, not a function that is implemented in the browser.
If I understand the documentation correctly, then for a browser project it should be enough to call the jsforce script via a script tag. You should not need any way to "require" files after that, since the jsforce script already contains everything you need. So you should be fine if you just remove the offending lines (i.e. all references to "require('jsforce');").
I have recently create an action and tested it in web simulator and on my Pixel 2 device. It is working fine for me. But during the review process the team at Google mentioned that while reviewing they found that error saying that my app isn't responding right now. Try again soon. (Screenshot attached). Can someone from the community please assist me on how to resolve the issue.
Below is the code in fullfillment, if this helps.
'use strict';
// Import the Dialogflow module from the Actions on Google client library.
const {dialogflow} = require('actions-on-google');
// Import the firebase-functions package for deployment.
const functions = require('firebase-functions');
// Instantiate the Dialogflow client.
const app = dialogflow({debug: true});
// Handle the Dialogflow intent named 'favorite color'.
// The intent collects a parameter named 'color'.
app.intent('think number', (conv, {nextStep}) => {
conv.close('The result is 9.');
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Screenshot of the response from Review Team:
Sometimes they are a bit quick to reject if there are network problems between them and Dialogflow, or if Dialogflow isn't responding. Make sure you turn on Dialogflow's logs to make sure there is no problem.
In general, the easiest thing to do if you haven't seen any errors and things appear to be working on your end is to:
Resubmit
Reply and tell them that you're using Dialogflow and there should be a reply.
var express = require('express');
var app = express();
var nodemailer=require('nodemailer');
var transporter = nodemailer.createTransport('smtps://username#gmail.com:password#smtp.gmail.com');
app.get('/sendMail', function(req,res){
var mailOptions={
to: 'receiver#gmail.com',
subject: 'Test Mail',
html: 'Testing the Mail'
}
transporter.sendMail(mailOptions,function(err,response){
if(err){
res.end('Mail not sent');
}
else{
res.end('Mail sent');
}
});
}).listen(9091);
console.log('This is running');
I am developing a firebase web app. I need to send an email via my web app. I saw some tutorials and found some code. Using the above code I am able to send emails. It runs in the port 9091. The message will be sent when the address bar has www.mywebsite.com/sendMail. But when I host this page to Firebase what changes to be done in this code, whether there must be a change in the port number?. Please help to implement this code in my web app.
Update 2019: Alternatively, you can use the new Trigger Email Firebase Extension (currently in beta).
As I mentioned on my comment, the problem is: your web app was built with NodeJS and you're trying to host it on a static host environment (Firebase Hosting). But Firebase also has Cloud Functions - a service that let's you run functions on a NodeJS environment. So you should deploy your app to that service instead.
It's really easy to Get Started with Cloud Functions. And since you have already set up Firebase hosting, you can simply go to that directory and set up Cloud Functions by using this command:
firebase init functions
A new folder named "functions" will be created and an index.js file will be automatically added to that folder. Now that's the file where you're going to write your functions (we're actually just gonna need 1 function triggered by HTTP to send the email).
So in order to create this function, you would no longer need Express. you can remove that and then change a bit your index.js file to add firebase-functions, so it would become:
//var express = require('express'); No longer needed
//var app = express(); No longer needed
var nodemailer=require('nodemailer');
const functions = require('firebase-functions');
var transporter = nodemailer.createTransport('smtps://username#gmail.com:password#smtp.gmail.com');
exports.sendMail = functions.https.onRequest((req, res) =>{
var mailOptions={
to: 'receiver#gmail.com',
subject: 'Test Mail',
html: 'Testing the Mail'
}
transporter.sendMail(mailOptions,function(err,response){
if(err){
res.end('Mail not sent');
}
else{
res.end('Mail sent');
}
});
});
Now when you want to send a message, you can use this url: https://us-central1-<project-id>.cloudfunctions.net/sendMail
Replace <project-id> with your Firebase Project ID.