Using LUIS with botkit conversations - azure-language-understanding

I am working on a bot and I am trying to use intents instead of patterns here
convo.ask('Do you want to eat a taco?', [
{
pattern: 'yes',
type: 'string',
handler: async(response, convo, bot) => {
return await convo.gotoThread('yes_taco');
}
},
{
pattern: 'no',
type: 'string',
handler: async(response, convo, bot) => {
return await convo.gotoThread('no_taco');
}
},
{
default: true,
handler: async(response, convo, bot) => {
await bot.say('I do not understand your response!');
// start over!
return await convo.repeat();
}
}
], {key: 'tacos'});
Is there a way to do that?

Check out this botkit-middleware-luis package. As the doc says, it replaces the pattern matching function with the returned intents from the LUIS call.
As demonstrated on the above link, you implementation would look something like this:
var luis = require('./lib/luis-middleware.js');
var luisOptions = {serviceUri: process.env.serviceUri};
controller.middleware.receive.use(luis.middleware.receive(luisOptions));
controller.hears(['hello','hi'],['direct_message','direct_mention','mention'], luis.middleware.hereIntent, function(bot,message) {
bot.reply(message,"Hello.");
});
Hope of help!

Related

importing medium articles into gatsby

I am trying to integrate my medium feed into gatsby and only want to select a few articles - not have the most recent ones. I was able to get the three most recent articles using this code:
index.config
mediumRssFeed:
"https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2F%40maxgraze",
shownArticles: 3,
Articles.js
const Articles = () => {
const MAX_ARTICLES = shownArticles
const { isIntroDone, darkMode } = useContext(Context).state
const [articles, setArticles] = useState()
const articlesControls = useAnimation()
// Load and display articles after the splashScreen sequence is done
useEffect(() => {
const loadArticles = async () => {
if (isIntroDone) {
await articlesControls.start({
opacity: 1,
y: 0,
transition: { delay: 1 },
})
fetch(mediumRssFeed, { headers: { Accept: "application/json" } })
.then(res => res.json())
// Feed also contains comments, therefore we filter for articles only
.then(data => data.items.filter(item => item.categories.length > 0))
.then(newArticles => newArticles.slice(0, MAX_ARTICLES))
.then(articles => setArticles(articles))
.catch(error => console.log(error))
}
}
loadArticles()
}, [isIntroDone, articlesControls, MAX_ARTICLES])
But I was hoping to query specific articles using gatsby-source-medium. However, it only returns 4 (and not even the most recent ones at that).
Is there a way to get all my articles via gatsby-source-medium? Otherwise, is there a way to "hard code" the articles I want? I'm not sure how to filter using the rss feed api. Thanks for yoru help!
As you suggested, there's a more native way using gatsby-source-medium but the documentation lacks good examples.
// In your gatsby-config.js
plugins: [
{
resolve: `gatsby-source-medium`,
options: {
username: `username/publication`,
},
},
]
Update:
It seems to be a known bug with Medium source and there's nothing we can do on our project. For further details:
gatsbyjs/gatsby#22491
A query like the following one will gather all posts from the user within the preview image:
query {
allMediumPost(sort: { fields: [createdAt], order: DESC }) {
edges {
node {
id
title
virtuals {
subtitle
previewImage {
imageId
}
}
author {
name
}
}
}
}
}

GraphQL mutation "Cannot set headers after they are sent to the client"

I'm implementing graphql login mutation to authenticate user login credential. Mutation verifies the password with bcrypt then sends a cookie to the client, which will render user profile based on whether the cookie is a buyer or owner user).
GraphQL Login Mutation Code:
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
loginUser: {
type: UserType,
args: {
email: { type: GraphQLString },
password: { type: GraphQLString }
},
resolve: function (parent, args, { req, res }) {
User.findOne({ email: args.email }, (err, user) => {
if (user) {
bcrypt.compare(args.password, user.password).then(isMatch => {
if (isMatch) {
if (!user.owner) {
res.cookie('cookie', "buyer", { maxAge: 900000, httpOnly: false, path: '/' });
} else {
res.cookie('cookie', "owner", { maxAge: 900000, httpOnly: false, path: '/' });
}
return res.status(200).json('Successful login');
} else {
console.log('Incorrect password');
}
});
}
});
}
}
}
});
Server.js:
app.use("/graphql",
(req, res) => {
return graphqlHTTP({
schema,
graphiql: true,
context: { req, res },
})(req, res);
});
Error message:
(node:10630) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[0] at ServerResponse.setHeader (_http_outgoing.js:470:11)
[0] at ServerResponse.header (/Users/xxx/xxx/server/node_modules/express/lib/response.js:771:10)
[0] at ServerResponse.append (/Users/xxx/xxx/server/node_modules/express/lib/response.js:732:15)
[0] at ServerResponse.res.cookie (/Users/xxx/xxx/server/node_modules/express/lib/response.js:857:8)
[0] at bcrypt.compare.then.isMatch (/Users/xxx/xxx/server/schema/schema.js:89:41)
I've done some research on this error, but can't seem to find a relevant answer. The issue seems to lie within response body being executing more than once, thus "cannot set headers after they are sent to the client". Since I'm sending both res.cookie() and res.status(200), how could I fix this problem?
express-graphql already sets the status and sends a response for you -- there's no need to call either res.status or res.json inside your resolver.
GraphQL always returns a status of 200, unless the requested query was invalid, in which case it returns a status of 400. If errors occur while executing the request, they will be included the response (in an errors array separate from the returned data) but the status will still be 200. This is all by design -- see additional discussion here.
Instead of calling res.json, your resolver should return a value of the appropriate type (in this particular case UserType), or a Promise that will resolve to this value.
Additionally, you shouldn't utilize callbacks inside resolvers since they are not compatible with Promises. If the bcrypt library you're using supports using Promises, use the appropriate API. If it doesn't, switch to a library that does (like bcryptjs) or wrap your callback inside a Promise. Ditto for whatever ORM you're using.
In the end, your resolver should look something like this:
resolve: function (parent, args, { req, res }) {
const user = await User.findOne({ email: args.email })
if (user) {
const isMatch = await bcrypt.compare(args.password, user.password)
if (isMatch) {
const cookieValue = user.owner ? 'owner' : 'buyer'
res.cookie('cookie', cookieValue, { maxAge: 900000, httpOnly: false, path: '/' })
return user
}
}
// If you want an error returned in the response, just throw it
throw new Error('Invalid credentials')
}

Mock specific graphql request in cypress when running e2e tests

When running e2e tests with Cypress, my goal is to mock a specific graphql query.
Currently, I can mock all requests like this:
cy.server();
cy.route('POST', '/graphql', {
data: {
foo: 'bar'
},
});
The problem is that this mocks all /graphql queries. It would be awesome if I somehow could say:
cy.route('POST', '/graphql', 'fooQuery', {
data: {
foo: 'bar'
},
});
In our application, we are using Apollo Graphql - and thus all queries are named.
With cypress 6.0 route and route2 are deprecated, suggesting the use of intercept. As written in the docs (https://docs.cypress.io/api/commands/intercept.html#Aliasing-individual-GraphQL-requests) you can mock the GraphQL requests in this way:
cy.intercept('POST', '/api', (req) => {
if (req.body.operationName === 'operationName') {
req.reply({ fixture: 'mockData.json'});
}
}
One way to go about it is to provide the mocked data for the graphql operations in question inside one fixture file
cypress/support/commands.js
Cypress.Commands.add('stubGraphQL', (graphQlFixture) => {
cy.fixture(graphQlFixture).then((mockedData) => {
cy.on('window:before:load', (win) => {
function fetch(path, { body }) {
const { operationName } = JSON.parse(body)
return responseStub(mockedData[operationName])
}
cy.stub(win, 'fetch', fetch).withArgs("/graphql").as('graphql');
});
})
})
const responseStub = result => Promise.resolve({
json: () => Promise.resolve(result),
text: () => Promise.resolve(JSON.stringify(result)),
ok: true,
})
//TODO how to get it to stop listening and trying to stub once the list of operations provided in fixture have been stubbed?
example fixture file cypress/fixtures/signInOperation.json (note that there are 2 operations in there and that's how you can specify which response to mock)
{
"SIGNIN_MUTATION": {
"data":{"signin":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"Sam","__typename":"User"}}
},
"CURRENT_USER_QUERY" : {
"data":{"me":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"!!Sam's Mock","permissions":["USER"],"cart":[{"id":"ck89gebgvse9w0981bhh4a147","quantity":5,"item":{"id":"ck896py6sacox0934lqc8c4bx","price":62022,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585253000/sickfitz/ecgqu4i1wgcj41pdlbty.jpg","title":"MensShoes","description":"Men's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gec6mb3ei0934lmyxne52","quantity":5,"item":{"id":"ck896os7oacl90934xczopgfa","price":70052,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585252932/sickfitz/i7ac6fqhsebxpmnyd2ui.jpg","title":"WomensShoes2","description":"Women's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gl45psely0981b2bvk6q5","quantity":7,"item":{"id":"ck89ghqkpb3ng0934l67rzjxk","price":100000,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585269417/sickfitz/eecjz883y7ucshlwvsbw.jpg","title":"watch","description":"Fancy Watch","__typename":"Item"},"__typename":"CartItem"}],"__typename":"User"}}
}
}
in your spec file
cy.stubGraphQL('signInOperation.json')
cy.visit(yourURL)
cy.get(loginButton).click()
With cypress 5.1, using the new route2 command it is very simple to mock GraphQL requests, for example:
cy.route2('/graphql', (req) => {
if(req.body.includes('operationName')){
req.reply({ fixture: 'mockData.json'});
}
});
I just added an if condition to evaluate if the body of the GraphQL request contains certain string as part of the query.
If that is true, then I reply back with a custom body loaded from a fixture.
Documentation for cy.route2():
https://docs.cypress.io/api/commands/route2.html
You can try this if want to use fixture for graphql response:
cy.intercept('POST', '/test_api/graphql', (req) => {
req.continue((res) => {
if (req.body.operationName === 'op_name') {
res.send({ fixture: 'MyFixture/xyz.json' }),
req.alias = 'graphql'
}
})
})

Is there a way to add conversation history when connecting to direct line?

We are using botframework-webchat v4. Is there any way to provide history that will be shown up in the chat?
This is currently what I have, but its not working, not sure what format should be for activities in store.
const store = window.WebChat.createStore(
{
activities: ['{"type":"message",...}']
},
({ dispatch }: { dispatch: any }) => (next: any) => (action: any) => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { activity } = action.payload;
if (activity.type === 'event' && activity.name === 'sample:backchannel') {
alert(JSON.stringify(activity, null, 2));
}
}
return next(action);
}
)
window.WebChat.renderWebChat(
{
directLine: this.directLine,
userID: this.userId,
styleOptions,
store
},
this.botWindowElement.nativeElement
);
Thanks in advance!!
You're solution above will, technically, work. Although, it's not very scalable in the long run. I would recommend you look over this BotFramework-WebChat experimental sample, Conversation History. It utilizes the sendConversationHistory API. This sample is a bit on the complex side, but will do precisely what you are wanting, i.e. load a previous user's conversation when a new session is started.
If you are wanting to reconnect a previous conversation (meaning continue a conversation using the same conversationId) then you should be aware that the Direct Line service has certain limitations. Reconnecting will only work for up to 14 days after the last activity to that conversation and only 24 hours if activities are present.
Hope of help!
#StevenKanberg Thanks for the help!
I found the answer in source code of BotFramework-WebChat.
Here is the sample,
test('absolute timestamp', async () => {
const activities = [
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000000',
timestamp: '2019-08-08T16:41:12.9397263Z',
from: {
id: 'dl_654b35e09ab4149595a70aa6f1af6f50',
name: '',
role: 'user'
},
textFormat: 'plain',
text: 'echo "Hello, World!"'
},
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000001',
timestamp: '2019-08-08T16:41:13.1835518Z',
from: {
id: 'webchat-mockbot',
name: 'webchat-mockbot',
role: 'bot'
},
text: 'Echoing back in a separate activity.'
},
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000002',
timestamp: '2019-08-08T16:41:13.3963019Z',
from: {
id: 'webchat-mockbot',
name: 'webchat-mockbot',
role: 'bot'
},
text: 'Hello, World!'
}
];
const styleOptions = { timestampFormat: 'absolute' };
const { driver } = await setupWebDriver({ storeInitialState: { activities }, props: { styleOptions } });

Writing Structural Expectations with Jest

I am looking to write what I am calling structural expectations with Jest and I am not sure how this could be accomplished.
To start I have a graphql server and a database with a number of todo items. I currently have the following test that just returns true if the content within the database is the same as the response that I have written. I want to check instead that the response looks like an object with data that could be anything.
Here is the code that I have:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: "message",
id: "5bd9aec8406e0a2170e04494",
dateCreated: "1540992712052",
dateDue: "1111111111"
},
{
message: "message",
id: "5bd9aeec60a9b2579882a308",
dateCreated: "1540992748028",
dateDue: "1111111111"
},
{
message: "new message",
id: "5bd9af15922b27236c91837c",
dateCreated: "1540992789836",
dateDue: "1111111111"
}
]
}
})
});
});
Now I want to write something like this, where there can be any number of returned items and they follow similar structuring:
describe('To Do:', () => {
it('add todo items', async () => {
const response = await axios.post('http://localhost:5000/graphql', {
query: `
query {
getTodoItems {
message
id
dateCreated
dateDue
}
}
`
});
const { data } = response;
expect(data).toMatchObject({
data: {
getTodoItems: [
{
message: expect.any(String),
id: expect.any(String),
dateCreated: expect.any(String),
dateDue: expect.any(String)
} // There needs to be unlimited additional items here
]
}
})
});
});
I have been looking throught the docs and I even tried nesting the expectations but I can't seem to get the desired response. Let me know what yo think or if I can clarify in any way.
I figured out the best way for me to do it. I would love to hear better answers. I wrote a function within the scope of the test as a jest.fn and then I called it. In that function, I made custom checks to parse the data that was received in the response. From there I added an expect function with the 'toHaveReturnedWith' method to see what the response of my custom function was and finishing out the test.
const addTodoResponse = jest.fn(() => {
// Custom parsing and check here
// Returns true or false
});
addTodoResponse();
expect(addTodoResponse).toHaveReturnedWith(true);
Are there better ways to do this out there?

Resources