I have been using expo to build a react native app and would like to integrate an IBM Watson chatbot onto my platform. When I import the module however I receive a lot of error messages as core node modules such as os and fs seem to be missing, but aren't downloaded with node.js for some reason. When I try and add these manually, the HTTPS module is missing the index.js file. Is there any way for me to find this file or resolve this problem another way?
It's not completely clear from your question but I shall assume that you are using the Node SDK for Watson Assistant. This is designed to be run in a Node.js environment which a react native JavaScript bundle is not (with or without expo). That's why you are missing key libraries like os and fs which the Node SDK expects. Installing fs won't resolve your problem because it also expects a Node.js environment to work, hence why there are react native specific fs libraries that are able to use ios and android code to interact with the file system of the phone.
What you should be attempting is running the Node SDK on an independent server and running simple api requests using libraries like axios or for more robust production systems graphql so that your architecture will approximate this high level design.
a high level architecture diagram which shows a phone connected by an arrow reading axios api request to another box labelled cloud hosted server. From this box another arrow labelled Node SDK is pointing to another box labelled Watson Assistant
Web applications are similarly limited. The code run on the user's browsers can't directly use the Watson Assistant SDK, these requests to Watson Assistant need to be run by a server. There is an example starter Watson Assistant web application that does this. If you run or host this application you can use the same server for your requests (although bear in mind this simple app and shared traffic probably isn't scalable for anything but a proof of concept).
So rather than running the api requests to Watson Assistant directly you run them to this domain of the server and then the necessary endpoint. The server in the example app is set up to accept requests to start the session at <your domain>/api/session and to send messages at <your domain>/api/message
You optionally can run direct api calls to Watson Assistant from a react native app without the SDK. It's not advisable because you would need to store your private keys on the device where they could be viewed by anyone.
Here is a functional component that is able to complete api calls direct to Watson Assistant using the v1 of the message tool without the SDK. BUT IT IS NOT ADVISED BECAUSE I MUST STORE MY KEYS INSECURELY.
import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import axios from 'axios';
import base64 from 'react-native-base64';
const workspace = ''; //replace with your own workspace id
const key = ''; //replace with your own key
const encodedKey = base64.encode(`apikey:${key}`);
// the following url will be different depending on where you host your Watson Assistant
// this is for Frankfurt as an example hence eu-de in the domain name
const assistantInstance =
'https://api.eu-de.assistant.watson.cloud.ibm.com/instances/<This is your own>/v1'; //replace with your own
const ExampleComponent = () => {
const [response, setResponse] = useState('');
const sendMessage = () => {
axios
.post(
`${assistantInstance}/workspaces/${workspace}/message?version=2018-09-20`,
{
input: { text: 'This is the message' },
},
{
headers: {
Authorization: `Basic ${encodedKey}`,
'Content-Type': 'application/json',
},
},
)
.then((data: any) => {
console.log(data);
setResponse('Got response');
})
.catch((err: any) => {
console.log(err);
setResponse('Got an error');
});
};
return (
<View>
<Text>{response}</Text>
<TouchableOpacity
onPress={() => {
sendMessage();
}}
>
Send message
</TouchableOpacity>
</View>
);
};
export default ExampleComponent;
The next complication you will find is that you will need to add code to store the context returned in the response otherwise your state is lost. Your body would end up looking something like
{
input: { text: 'This is the message' },
context: savedContextObject
},
The newer version of the API has a stateful version which you may want to use instead. You can use this axios as a pattern for constructing whatever requests your prefer.
For the third and final time PLEASE DO NOT SAVE TO THE JS FILE YOUR API KEY as I do here. This is just as an example and for proof of concepts. Anyone who downloads your app will be able to unzip your apk and read these strings in your generated JS bundle unencrypted!
Related
I built a simple react app with "create-react-app" and I want to use serverless functions with netlify.
I use DataStax Astra Cassandra DB for that purpose, and created a netlify.toml config and .env variables (for the Database) inside my react project.
I set up a serverless functions folder for netlify:
const { createClient } = require('#astrajs/collections')
// create an Astra client
exports.handler = async function (event, context) {
try {
const astraClient = await createClient({
astraDatabaseId: process.env.ASTRA_DB_ID,
astraDatabaseRegion: process.env.ASTRA_DB_REGION,
applicationToken: process.env.ASTRA_DB_APPLICATION_TOKEN,
})
// const basePath = `/api/rest/v2/KEYSPACES/${process.env.ASTRA_DB_KEYSPACE}/collections/messages`
const messagesCollection = astraClient
.namespace(process.env.ASTRA_DB_KEYSPACE)
.collection('messages')
const message = await messagesCollection.create('msg1', {
text: 'hello my name is Marc!',
})
return {
statusCode: 200,
body: JSON.stringify(message),
}
} catch (e) {
console.error(e)
return {
statusCode: 500,
body: JSON.stringify(e),
}
}
it works when I run netlify dev , then my .env variables are injected into the .js file.
However, I am wondering if I should use the nodejs datastax collections here, or the REST API functions from datastax (https://docs.datastax.com/en/astra/docs/astra-collection-client.html)? Because with react, it's essentially running in the browser or not? I am wondering why this still works with nodejs (because its not a nodejs environment with react, or is it?)
I am getting access to my functions via localhost:8888/.netlify/functions/functionName is this served from a nodejs server or is it browser stuff?
it works when I run netlify dev , then my .env variables are injected into the .js file. However, I am wondering if I should use the nodejs datastax collections here, or the REST API functions from datastax (https://docs.datastax.com/en/astra/docs/astra-collection-client.html)? Because with react, it's essentially running in the browser or not?
Correct - you would expose your Astra credentials to the world if you connect to your database directly from your React app.
I am wondering why this still works with nodejs (because its not a nodejs environment with react, or is it?) I am getting access to my functions via localhost:8888/.netlify/functions/functionName is this served from a nodejs server or is it browser stuff?
Netlify functions run serverside so it is safe to connect to Astra in your function code. Here's an example: https://github.com/DataStax-Examples/todo-astra-jamstack-netlify/blob/master/functions/createTodo.js
I want to make a POST (add a new task) through chatting with Watson.
I have the POST function which works very well, I tested it on Postman. And I have the Watson nodes created.
Here is my endpoint from the POST in Node js:
MainRouter.post('/welcome', (req, res) => {
TaskPost.postTask(req.body).then(message => {
return res.json(message);
}).catch((error) => {
return res.status(404).json(error);
});
});
Here is my conversation with Watson (is working very well):
I also included this in the main.js and changed the password and url:
const AssistantV2 = require('ibm-watson/assistant/v2');
const { IamAuthenticator } = require('ibm-watson/auth');
const assistant = new AssistantV2({
authenticator: new IamAuthenticator({ apikey: '<apikey>' }),
serviceUrl: 'https://api.us-south.assistant.watson.cloud.ibm.com',
version: '2018-09-19'
});
How do I make this connection ? What do I have to include?
I would recommend the official Node.js SDK for the IBM Watson services. It includes support for Watson Assistant and is easy to use.
If you still want to use the V2 API directly, then take a look at how the SDK utilizes the API. As alternative, the API docs for Watson Assistant have code snippets for Node.js.
The basic flow is that you authenticate, then establish a session, and finally send messages with the user input to Watson Assistant.
To get to your dialog node with the task, the user (or your code) would need to send the right intents and entities for navigating from the root node to that specific subnode.
I am really new to Angular. I am trying to create a service which i want to consume in my angular component. While doing so i am getting below error.
Below is my code which i am writing
import { Injectable } from '#angular/core';
import { HttpClient} from '#angular/common/http';
import { CosmosClient } from '#azure/cosmos';
import {Observable,of} from 'rxjs'
#Injectable({
providedIn: 'root'
})
export class ApiService {
databaseId='dbName';
containerId='Container Name';
constructor() { }
public async getProjects():Promise<Observable<any>>{
const endpoint = "https://AAAA.documents.azure.com:443/";
const key = "==";
const client = new CosmosClient({ endpoint, key });
const database = client.database(this.databaseId);
const container = database.container(this.containerId);
const querySpec = {query: "SELECT * from c where c.Category=\"Details\""};
const { resources:items } = await container.items.query(querySpec).fetchAll();
return of(items);
}
}
Any help is really appreciated.
There is an exception to every rule, but the use cases for connecting to a DB directly from a web browser are pretty close to zero. By doing so, you lose all fine grained control over what a user can do in your database and the only way to revoke access is to rotate your keys. You may not currently have anything in your database that is sensitive, but it is still considered bad practice.
For this reason, the CosmosDB library is compatible with NodeJS as a server-side framework. Whether or not it works with front end frameworks like Angular or React are incidental. There are some large changes in how Angular compiles projects in version 9, and it looks like the Cosmos client is not compatible with the new Ivy compiler.
You do have a couple options here.
(recommended) Use an API layer between your database and your front end. You can use Node to keep it within Javascript. If you are running on Azure, there are services like Azure Functions that can make this even easier to implement securely, or you can run it from the same App Service, VM, or whatever hosting solution you are using.
Disable the new Ivy compiler.You can do this by adding aot: false in your angular.json
I am testing Dialogflow Fulfillment with the Inline Editor.
What I am trying to do is a http request using que 'request' library.
Here is the code I am using:
const requesthttp = require('request');
requesthttp('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY', { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
console.log(body.url);
console.log(body.explanation);
});
But It returns me an error of not found.
I also noticed an alert on my Dialogflow with the following message:
"Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions."
So... Probably I can't test this piece of code without configuring a billing account.
My question is... Is there a url that I can use to test this code?
Or the only way for me to test this code is configuring a billing account and paying for it?
Thanks in advance
There are a number of approaches to testing your code.
If you want to continue to use Dialogflow's Inline Editor, you will need to setup Firebase to use a payment plan. However, the Blaze plan is "pay as you go" after a basic level of use. This level of use should be sufficient to cover most testing (and even very light production) uses of the service without imposing a charge. Once your Action has been approved, you're able to receive credits for the Google Cloud Platform, which can be applied to this use in case you go over the minimum level.
You can also use Firebase Cloud Functions, which the Inline Editor is based on, and your own local editor. One advantage of this is that you can serve the function locally, which has many of the same features as deploying it, but doesn't have the URL restriction (it is your own machine, after all). You can use a tool such as ngrok to create a secure tunnel to your your machine during testing. Once you have tested, you can deploy this to Firebase with a paid plan.
You can, of course, choose to use any other hosting method you wish. Google and Dialogflow allow you to run your fulfillment webhook on any server, as long as that server can provide an HTTPS connection using a valid, non-self-signed, certificate. If you're using node.js, you can continue to use these libraries. If you wish to use another language, you will need to be able to parse and return JSON, but otherwise you have no restrictions.
There are a lot of ways to create your own server like using NodeJS client with Express.JS which you can expose to the internet using NGROK as webhook for fulfilment.
Develop a webhook. You can use different client libraries in NodeJS (AoG Client or Dialogflow Client) or in Python (Flask-Assistant or Dialogflow Client) or can create your own just using JSON request/response with Dialogflow and Action-on-Google.
Once the webhook is ready, run it locally and expose to the internet using NGROK.
Start with following code for Actions-on-Google with Express.JS
'use strict';
const {dialogflow} = require('actions-on-google');
const express = require('express');
const bodyParser = require('body-parser');
const app = dialogflow();
app.intent('Default Welcome Intent', conv => {
conv.ask('Hi, Welcome to Assistant by Express JS ');
});
express().use(bodyParser.json(), app).listen(8080);
Since DF uses firebase cloud functions you can use https as in nodejs. But requesting domains outside of the google/firebase universe will require the paid version of firebase.
const https = require('https');
return new Promise((resolve, reject) => {
const hostname = info.hostname;
const pathname = info.pathname;
let data = '';
const request = https.get(`https://${hostname}${pathname}`, (res) => {
res.on('data', (d) => {
data += d;
});
res.on('end', resolve);
});
request.on('error', reject);
});
I am having trouble implementing the isRequestFromAssistant method to verify requests to my fulfillment webhook. Using Node.js, I instantiate the following variables at the start of my index.js file:
const App = require('actions-on-google').ApiAiApp;
const app = new App({ request, response });
I then use "app" with the .ask and .tell and other methods throughout my functions.
The code I see in the docs for implementing isRequestFromAssistant is:
const app = new ActionsSdkApp({request, response});
app.isRequestFromAssistant('my-project-id')
.then(() => {
app.ask('Hey there, thanks for stopping by!');
})
.catch(err => {
response.status(400).send();
});
If I leave out the first line and use my existing app variable, created with the .ApiAi method instead of the .ActionsSdkApp method, it doesn't work. If I create a new variable App1 and app1 using the .ActionsSdkApp method and change the above code to be app1.isRequestFromAssistant, it also doesn't work. I have tried other variations with no luck.
When I say it doesn't work, I mean I receive a 500 Internal Server Error when I call it. I am hosting it with NGROK currently. I am still a beginner with Node.js, although I have managed to get the other 700 lines of code working just fine, learning mostly from Google searches and reading these forums.
You have a few things going on here which, individually or separately, may be causing the problem.
First - make sure you have the most recent version of the actions-on-google library. The isRequestFromAssistant() function was added in version 1.6.0, I believe.
Second - Make sure you're creating the right kind of App instance. If you're using Dialogflow (formerly API.AI), you should be creating it with something like
const App = require('actions-on-google').DialogflowApp;
const app = new App( {request, response} );
or
const { DialogflowApp } = require('actions-on-google');
const app = new DialogflowApp( {request, response} );
(They both do the same thing, but you'll see both forms in documentation.) You should switch to DialogflowApp from ApiAiApp (which your example uses) to reflect the new name, but the old form has been retained.
If you're using the Actions SDK directly (not using Dialogflow / API.AI), then you should be using the ActionsSdkApp object, something like
const { ActionsSdkApp } = require('actions-on-google');
const app = new ActionsSdkApp({request: request, response: response});
(Again, you'll see variants on this, but they're all fundamentally the same.)
Third - Make sure you're using the right function that matches the object you're using. The isRequestFromAssistant() function is only if you are using the Actions SDK.
If you are using Dialogflow, the corresponding function is isRequestFromDialogflow(). The parameters are different, however, since it requires you to set confirmation information as part of your Dialogflow configuration.
Finally - If you're getting a 500 error, then check your logs (or the output from stderr) for the node.js server that is running. Typically there will be an error message there that points you in the right direction. If not - posting that error message as part of your StackOverflow question is always helpful.
Set the secure (randomly generated) auth header & key values in the dialogflow Fulfillment page, then in nodejs:
if (app.isRequestFromDialogflow("replace_with_key", "replace_with_value")) {
console.log("Request came from dialogflow!");
// rest of bot
} else {
console.log("Request did not come from dialogflow!");
response.status(400).send();
}
Also see: https://developers.google.com/actions/reference/nodejs/DialogflowApp#isRequestFromDialogflow