Wanting to chain web requests and pass data down through them in Twilio Studio - twilio-studio

So I'm playing with Twilio Studio, and building a sample IVR. I have it doing a web request to an API that looks up the customer based on their phone number. That works, I can get/say their name to them.
I'm having trouble with the next step, I want to do another http request and pass the 'customer_id' that I get in webrequest1 to webrequest2, but it almost looks like all the web requests fire right when the call starts instead of in order/serialized.
It looks sorta like this;
call comes in, make http request to lookup customer (i get their customer_id and name)
split on content, if customer name is present, (it is, it goes down this decision path)
do another http request to "get_open_invoice_count", this request needs the customer_id though and not their phone number.
From looking at the logs it's always got a blank value there, even though in the "Say" step just above I can say their customer_id and name.
I can almost imagine someone is going to say I should go use a function, but for some reason I can't get a simple function to do a (got) get request.
I've tried to copy/paste this into a function and I kind of think this example is incomplete: https://support.twilio.com/hc/en-us/articles/115007737928-Getting-Started-with-Twilio-Functions-Beta-
var got = require('got');
got('https://swapi.co/api/people/?search=r2', {json: true})
.then(function(response) {
console.log(response)
twiml.message(response.body.results[0].url)
callback(null, twiml);
})
.catch(function(error) {
callback(error)
})
If this is the right way to do it, I'd love to see one of these ^ examples that returns json that can be used in the rest of the flow. Am I missing something about the execution model? I'm hoping it executes step by step as people flow through the studio, but I'm wondering if it executes the whole thing at boot?
Maybe another way to ask this question is; If I wanted to have the IVR be like
- If I know who you are, i send you down this path, if I know who you are I want to lookup some account details and say them to you and give you difference choices than if you are a stranger.
---- how do you do this?

You're right -- that code excerpt from the docs is just a portion that demonstrates how you might use the got package.
That same usage in context of the complete Twilio Serverless Function could look something like this:
exports.handler = function(context, event, callback) {
var twiml = new Twilio.twiml.MessagingResponse();
var got = require('got');
got('https://example.com/api/people/?search=r2', { json: true })
.then(function(response) {
console.log(response);
twiml.message(response.body.results[0].url);
callback(null, twiml);
})
.catch(function(error) {
callback(error);
});
};
However, another part of the issue here is that the advice in this documentation is perfectly reasonable for Functions when building an app on the Twilio Runtime, but there are a couple of unsaid caveats when invoking these functions from a Studio Flow context. Here's some relevant docs about that: https://support.twilio.com/hc/en-us/articles/360019580493-Using-Twilio-Functions-to-Enhance-Studio-Voice-Calls-with-Custom-TwiML
This function would be acceptable if you were calling it directly from an inbound number, but when you use the Function widget within a Studio flow to return TwiML, Studio releases control of the call.
If you want to call external logic that returns TwiML from a flow, and want to return to that flow later, you need to use the TwiML Redirect widget (see "Returning control to Studio" for details).
However, you don't have to return TwiML to Studio when calling external logic! It sounds like you want to make an external call to get some information, and then have your Flow direct the call down one path or another, based on that information. When using a Runtime Function, just have the function return an object instead of twiml, and then you can access that object's properties within your flow as liquid variables, like {{widgets.MY_WIDGET_NAME.parsed.PROPERTY_NAME}}. See the docs for the Run Function widget for more info. You would then use a "Split Based On..." widget following the function in your flow to direct the call down the desired branch.
The one other thing to mention here is the Make HTTP Request widget. If your Runtime Function is just wrapping a call to another web service, you might be able to get away with just using the widget to call that service directly. This works best when the service is under your control, since then you can ensure that the returned data is in a format that is usable to the widget.

Related

Displaying data from Firebase on load of Ionic app

I'm a beginner in Ionic and Firebase. To learn using ionic+firebase, I'm writing a RandomQuote app to fetch a random entry from Firebase. A reload() method is called when I click a reload button, and the random quote is displayed as expected.
However, I also want the quote to display when the app is loaded, i.e., before I click the reload button. I call the reload() method in the constructor but it doesn't work. I have tried to search for answers on the web but cannot find anything that I could understand. Not sure if I'm searching the wrong keywords or in the wrong domains.
The following is the reload() method that I put in my FirebaseProvider class and called from my home.ts:
reload(){
this.afd.list('/quoteList/').valueChanges().subscribe(
data => {
this.oneQuote = data[Math.floor(Math.random() * data.length)];
}
)
return this.oneQuote;
}
Can anyone give me some hints? Or any pointer to useful books / materials for beginners will also be highly appreciated. Thank you very much.
Data is loaded from Firebase asynchronously. This means that by the time your return statement runs this.oneQuote doesn't have a value yet.
This is easiest to say by placing a few log statements around your code:
console.log("Before subscribing");
this.afd.list('/quoteList/').valueChanges().subscribe(
data => {
console.log("Got data");
}
)
console.log("After subscribing");
When you run this code, the output is:
Before subscribing
After subscribing
Got data
This is probably not what you expected. But it completely explains why your return statement doesn't return the data: that data hasn't been loaded yet.
So you need to make sure your code that needs the data runs after the data has been loaded. There are two common ways to do this:
By moving the code into the callback
By returning a promise/subscription/observable
Moving the code into the callback is easiest: when the console.log("Got data") statement runs in the code above, the data is guaranteed to be available. So if you move the code that requires the data into that place, it can use the data without problems.
Returning a promise/subscription/observable is a slightly trickier to understand, but nicer way to doing the same. Now instead of moving the code-that-needs-data into the callback, you'll return "something" out of the callback that exposes the data when it is available. In the case of AngularFire the easiest way to do that is to return the actual observable itself:
return this.afd.list('/quoteList/').valueChanges();
Now the code that needs the quotes can just subscribe to the return value and update the UI:
reload().subscribe(data => {
this.oneQuote = data[Math.floor(Math.random() * data.length)];
}
A final note: having a reload() method sounds like an antipattern. The subscription will already be called whenever the data in the quoteList changes. There is no need to call reload() for that.

Meteor: show maintenance page in client side from a server side variable

I want to use reactive information on the client side to disable some functionalities while the server is performing an important heavy task.
I tried using a publication but even though the subscription in the client was inside an autorun it was not updating the field. I’m not sure if using a publication is the best option.
Let’s say I have a server variable called “IN_MAINTENANCE” and in the client side I want to load a specific template for the maintenance page, but never allowing the user to change this (The variable should only be defined in the server side).
How can I achieve this without storing anything in the Database?
Pretty sure you have to use the database for it to be a reactive update that applies to all users. You could use a settings variable, but that would require an app restart to take effect. This answer has code you can repurpose for this.
I think your first instinct was right in doing a publication. Reacting to a data change is the only way for the server to influence the client. Make sure that your publish and subscribe is working correctly, and then in your autorun function you can take the user (and all users) to the maintenance page.
This is a Meteor brain teaser right? The "without storing anything in the db" is the interesting bit.
Let's say that you have some publication that all clients are getting, whether logged in or not, and that is expected to return at least one document:
Meteor.publish('default',function(){
let query = ...
let options = ...
return MyCollection.find(query,options);
});
You could signal to the client by just returning nothing:
Meteor.publish('default',function(){
if ( IN_MAINTENANCE ) {
return;
} else {
let query = ...
let options = ...
return MyCollection.find(query,options);
}
});
Then on the client you just look to see that the subscription is ready but empty and switch layouts based on that.
There are 100 solutions to this puzzle, this is just a particularly hacky one.

How can I use collection.find as a result of a meteor method?

I'm trying to follow the "Use the return value of a Meteor method in a template helper" pattern outlined here, except with collections.
Essentially, I've got something like this going:
(server side)
Meteor.methods({
queryTest: function(selector) {
console.log("In server meteor method...");
return MyCollection.find(selector);
}
});
(client side)
Meteor.call('queryTest', {}, function(error, results) {
console.log("in queryTest client callback...");
queryResult = [];
results.forEach(function(result) {
// massage it into something more useful for display
// and append it to queryResult...
});
Session.set("query-result", queryResult);
});
Template.query_test_template.helpers({
query_test_result: function() {
return Session.get("query-result");
}
});
The problem is, my callback (from Meteor.call) doesn't even get invoked.
If I replace the Method with just 'return "foo"' then the callback does get called. Also, if I add a ".fetch()" to the find, it also displays fine (but is no longer reactive, which breaks everything else).
What gives? Why is the callback not being invoked? I feel like I'm really close and just need the right incantation...
If it at all matters: I was doing all the queries on the client side just fine, but want to experiment with the likes of _ensureIndex and do full text searches, which from what I can tell, are basically only available through server-side method calls (and not in mini-mongo on the client).
EDIT
Ok, so I migrated things publish/subscribe, and overall they're working, but when I try to make it so a session value is the selector, it's not working right. Might be a matter of where I put the "subscribe".
So, I have a publish that takes a parameter "selector" (the intent is to pass in mongo selectors).
On the client, I have subscribe like:
Meteor.subscribe('my-collection-query', Session.get("my-collection-query-filter"));
But it has spotty behaviour. On one article, it recommended putting these on Templates.body.onCreate. That works, but doesn't result in something reactive (i.e. when I change that session value on the console, it doesn't change the displayed value).
So, if I follow the advice on another article, it puts the subscribe right in the relevant helper function of the template that calls on that collection. That works great, but if I have MULTIPLE templates calling into that collection, I have to add the subscribe to every single one of them for it to work.
Neither of these seems like the right thing. I think of "subscribing" as "laying down the pipes and just leaving them there to work", but that may be wrong.
I'll keep reading into the docs. Maybe somewhere, the scope of a subscription is properly explained.
You need to publish your data and subscribe to it in your client.
If you did not remove "autopublish" yet, all what you have will automatically be published. So when you query a collection on client (in a helper method for example), you would get results. This package is useful just for quick development and prototyping, but in a real application it should be removed. You should publish your data according to your app's needs and use cases. (Not all users have to see all data in all use cases)

Force iron-router to get back an ready from waitOn

Currently it seems not to be possible to force a ready() state in the route. For example:
I have a waitOn on 2 subscribtions. One of them returns a Meteor.Error - now the route will be in the loading-state with no ending.
Is there a recommend way to tell iron-router "waitOn until subscribtion is ready OR subscribtion fails with an error" ?
Edit:
To explain my special case:
The waitOn is for a route which is for searching. The search arguments are "what" and "where". In "where" I have a plan String Address and need to convert it to a geo coordinate. For this I use the googlemaps converter on the Serverside (because its Sync). When no address was found I need to get back a error a lá "This address must be wrong". For this I need the functionality to get back an error.
When I do it like David Weldon said I need to do this step in the waitOn method but the Client-Side googlemaps converter is not Sync - instead its async so this would not work.
General Recommendations
It's okay for your publishers to throw errors, but those conditions should only be hit if the client does the wrong thing. In other words, you are solving the wrong problem - you should only subscribe when you know the publisher will not throw an error. Let's look at an example:
Suppose your route needs to subscribe to newPosts and postsForSuperuser. Assume that the postsForSuperuser publisher will throw an error if the user isn't a superuser. It's now the client's job not to let that happen. The waiton definition could look like:
waitOn: function() {
var subs = [Meteor.subscribe('newPosts')];
if (Roles.userIsInRole(Meteor.user(), ['superuser']))
subs.push(Meteor.subscribe('postsForSuperuser'));
return subs;
}
Because we are conditionally adding the postsForSuperuser subscription, we don't give the publisher the opportunity to throw an error.
Your specific use case
You case is a little more tricky, because mechanically the client is doing the correct thing but the user input may happen to be bad. In this case, I don't think throwing an error is appropriate. Here are some recommendations:
Avoid the problem by checking the address via a method call prior to changing the route.
If an address is found to be invalid, have the publish function immediately return this.ready(). This will prevent your route from failing, but you'll be left assuming that the reason you have no data is because of the address. If that's a valid assumption (i.e. it's the only possible reason for failure), then your router could deal with this by using a dataNotFound hook.
If you need to explicitly identify the cause of the error, have a close look at the 'counts' example from the docs. You can declare a client-only collection called addressErrors and then call this.added with a dynamically created document describing the cause of the error. The implementation of this is a little more tricky, and probably worthy of a separate question if you get stuck. I'd see if the first two make sense before attempting it.

Update document in Meteor mini-mongo without updating server collections

In Meteor, I got a collection that the client subscribes to. In some cases, instead of publishing the documents that exists in the collection on the server, I want to send down some bogus data. Now that's fine using the this.added function in the publish.
My problem is that I want to treat the bogus doc as if it were a real document, specifically this gets troublesome when I want to update it. For the real docs I run a RealDocs.update but when doing that on the bogus doc it fails since there is no representation of it on the server (and I'd like to keep it that way).
A collection API that allowed me to pass something like local = true this would be fantastic but I have no idea how difficult that would be to implement and I'm not to fond of modifying the core code.
Right now I'm stuck at either creating a BogusDocs = new Meteor.Collection(null) but that makes populating the Collection more difficult since I have to either hard code fixtures in the client code or use a method to get the data from the server and I have to make sure I call BogusDocs.update instead of RealDocs.update as soon as I'm dealing with bogus data.
Maybe I could actually insert the data on the server and make sure it's removed later, but the data really has nothing to do with the server side collection so I'd rather avoid that.
Any thoughts on how to approach this problem?
After some further investigation (the evented mind site) it turns out that one can modify the local collection without making calls to the server. This is done by running the same methods as you usually would, but on MyCollection._collection instead of just on Collection. MyCollection.update() would thus become MyCollection._collection.update(). So, using a simple wrapper one can pass in the usual arguments to a update call to update the collection as usual (which will try to call the server which in turn will trigger your allow/deny rules) or we can add 'local' as the last argument to only perform the update in the client collection. Something like this should do it.
DocsUpdateWrapper = function() {
var lastIndex = arguments.length -1;
if (arguments[lastIndex] === 'local') {
Docs._collection.update(arguments.slice(0, lastIndex);
} else {
Docs.update(arguments)
}
}
(This could of course be extended to a DocsWrapper that allows for insertion and removals too.)(Didnt try this function yet but it should serve well as an example.)
The biggest benefit of this is imo that we can use the exact same calls to retrieve documents from the local collection, regardless of if they are local or living on the server too. By adding a simple boolean to the doc we can keep track of which documents are only local and which are not (An improved DocsWrapper could check for that bool so we could even omit passing the 'local' argument.) so we know how to update them.
There are some people working on local storage in the browser
https://github.com/awwx/meteor-browser-store
You might be able to adapt some of their ideas to provide "fake" documents.
I would use the transform feature on the collection to make an object that knows what to do with itself (on client). Give it the corruct update method (real/bogus), then call .update rather than a general one.
You can put the code from this.added into the transform process.
You can also set up a local minimongo collection. Insert on callback
#FoundAgents = new Meteor.Collection(null, Agent.transformData )
FoundAgents.remove({})
Meteor.call 'Get_agentsCloseToOffer', me, ping, (err, data) ->
if err
console.log JSON.stringify err,null,2
else
_.each data, (item) ->
FoundAgents.insert item
Maybe this interesting for you as well, I created two examples with native Meteor Local Collections at meteorpad. The first pad shows an example with plain reactive recordset: Sample_Publish_to_Local-Collection. The second will use the collection .observe method to listen to data: Collection.observe().

Resources