Skip to content
Permalink
Browse files

Fixing tests, improved README and examples

  • Loading branch information...
Depado committed Mar 14, 2018
1 parent d3c24c0 commit 62b7a136ee6dfbdbeaac2519003de90b61910ded
Showing with 165 additions and 18 deletions.
  1. +122 −1 README.md
  2. +9 −9 dialogflow_test.go
  3. +27 −1 examples/gin/main.go
  4. +6 −6 location.go
  5. +1 −1 location_test.go
123 README.md
@@ -14,6 +14,22 @@ Simple library to create compatible DialogFlow v2 webhooks using Go.
This package is only intended to create webhooks, it doesn't implement the whole
DialogFlow API.

<!-- TOC depthFrom:2 -->

- [Introduction](#introduction)
- [Goal of this package](#goal-of-this-package)
- [Disclaimer](#disclaimer)
- [Installation](#installation)
- [Using dep](#using-dep)
- [Using go get](#using-go-get)
- [Usage](#usage)
- [Handling incoming request](#handling-incoming-request)
- [Retrieving params and contexts](#retrieving-params-and-contexts)
- [Responding with a fulfillment](#responding-with-a-fulfillment)
- [Examples](#examples)

<!-- /TOC -->

## Introduction

### Goal of this package
@@ -55,11 +71,116 @@ All the following examples and usages use the `df` notation.

### Handling incoming request

In this section we'll use the [gin](https://github.com/gin-gonic/gin) router as
it has some nice helper functions that will keep the code concise. For an
example using the standard `http` router, see
[this example](https://github.com/leboncoin/dialogflow-go-webhook/blob/master/examples/http).

When DialogFlow sends a request to your webhook, you can unmarshal the incoming
data to a `df.Request`.
data to a `df.Request`. This, however, will not unmarshal the contexts and the
parameters because those are completely dependent on your data models.

```go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
df "github.com/leboncoin/dialogflow-go-webhook"
)
type params struct {
City string `json:"city"`
Gender string `json:"gender"`
Age int `json:"age"`
}
func HandleWebhook(c *gin.Context) {
var err error
var dfr *df.Request
if err = c.BindJSON(&dfr); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
}
func main() {
r := gin.Default()
r.POST("/webhook")
if err := r.Run("127.0.0.1:8001"); err != nil {
panic(err)
}
}
```

### Retrieving params and contexts

```go
type params struct {
City string `json:"city"`
Gender string `json:"gender"`
Age int `json:"age"`
}
func HandleWebhook(c *gin.Context) {
var err error
var dfr *df.Request
var p params
if err = c.BindJSON(&dfr); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
if err = dfr.GetParams(&p); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
}
```

In this example we're getting the DialogFlow request and unmarshalling the
params to a defined struct. This is why `json.RawMessage` is used in both the
`Request.QueryResult.Parameters` and in the `Request.QueryResult.Contexts`.

This also allows you to filter and route according to the `action` and `intent`
DialogFlow detected, which means that depending on which action you detected,
you can unmarshal the parameters and contexts to a completely different data
structure.

The same thing can be done for contexts :

```go
type params struct {
City string `json:"city"`
Gender string `json:"gender"`
Age int `json:"age"`
}
func HandleWebhook(c *gin.Context) {
var err error
var dfr *df.Request
var p params
if err = c.BindJSON(&dfr); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
if err = dfr.GetContext("my-amazing-context", &p); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}
}
```

### Responding with a fulfillment

DialogFlow expects you to respond with what is called a [fulfillment](https://dialogflow.com/docs/reference/api-v2/rest/v2beta1/WebhookResponse).


## Examples

- [Using Gin](https://github.com/leboncoin/dialogflow-go-webhook/blob/master/examples/gin)
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)

func TestResponse_GetParams(t *testing.T) {
func TestRequest_GetParams(t *testing.T) {
type out struct {
In string `json:"in"`
Out string `json:"out"`
@@ -26,18 +26,18 @@ func TestResponse_GetParams(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rw := &Response{QueryResult: QueryResult{Parameters: tt.params}}
rw := &Request{QueryResult: QueryResult{Parameters: tt.params}}

var output out
if err := rw.GetParams(&output); (err != nil) != tt.expectError {
t.Errorf("Response.GetParams() error = %v, wantErr %v", err, tt.expectError)
t.Errorf("Request.GetParams() error = %v, wantErr %v", err, tt.expectError)
}
assert.Equal(t, output, tt.expected, "should match")
})
}
}

func TestResponse_GetContext(t *testing.T) {
func TestRequest_GetContext(t *testing.T) {
type out struct {
In string `json:"in"`
Out string `json:"out"`
@@ -76,18 +76,18 @@ func TestResponse_GetContext(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rw := &Response{QueryResult: QueryResult{OutputContexts: tt.fields}}
rw := &Request{QueryResult: QueryResult{OutputContexts: tt.fields}}

var output out
if err := rw.GetContext(tt.ctx, &output); (err != nil) != tt.wantErr {
t.Errorf("Response.GetContext() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("Request.GetContext() error = %v, wantErr %v", err, tt.wantErr)
}
assert.Equal(t, output, tt.expected, "should match")
})
}
}

func TestResponse_NewContext(t *testing.T) {
func TestRequest_NewContext(t *testing.T) {
type out struct {
In string `json:"in"`
Out string `json:"out"`
@@ -128,15 +128,15 @@ func TestResponse_NewContext(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rw := &Response{
rw := &Request{
Session: tt.fields.Session,
ResponseID: tt.fields.ResponseID,
QueryResult: tt.fields.QueryResult,
OriginalDetectIntentRequest: tt.fields.OriginalDetectIntentRequest,
}
got, err := rw.NewContext(tt.args.name, tt.args.lifespan, tt.args.params)
if (err != nil) != tt.wantErr {
t.Errorf("Response.NewContext() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("Request.NewContext() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
@@ -7,7 +7,7 @@ import (
df "github.com/leboncoin/dialogflow-go-webhook"
)

type MyParams struct {
type params struct {
City string `json:"city"`
Gender string `json:"gender"`
Age int `json:"age"`
@@ -16,15 +16,41 @@ type MyParams struct {
func HandleWebhook(c *gin.Context) {
var err error
var dfr *df.Request
var p params

if err = c.BindJSON(&dfr); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}

// Retrieve the params of the request
if err = dfr.GetParams(&p); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}

// Retrieve a specific context
if err = dfr.GetContext("my-awesome-context", &p); err != nil {
c.AbortWithStatus(http.StatusBadRequest)
return
}

// Do things with the context you just retrieved

// Send back a fulfillment
dff := &df.Fulfillment{
FulfillmentMessages: df.Messages{
df.ForGoogle(df.SingleSimpleResponse("hello", "hello")),
{RichMessage: df.Text{Text: []string{"hello"}}},
},
}
c.JSON(http.StatusOK, dff)
}

func main() {
r := gin.Default()
r.POST("/webhook")
if err := r.Run("127.0.0.1:8001"); err != nil {
panic(err)
}
}
@@ -4,12 +4,12 @@ import "encoding/json"

// Location is a location object sent back by DialogFlow
type Location struct {
Simple string
Region string `json:"admin-area,omitempty"`
RegionOriginal string `json:"admin-area.original,omitempty"`
RegionObject json.RawMessage `json:"admin-area.object,omitempty"`
DepartmentOriginal string `json:"subadmin-area.original,omitempty"`
Department string `json:"subadmin-area,omitempty"`
Simple string
AdminArea string `json:"admin-area,omitempty"`
AdminAreaOriginal string `json:"admin-area.original,omitempty"`
AdminAreaObject json.RawMessage `json:"admin-area.object,omitempty"`
SubAdminArea string `json:"subadmin-area,omitempty"`
SubAdminAreaOriginal string `json:"subadmin-area.original,omitempty"`
}

// UnmarshalJSON implements the Unmarshaler interface for JSON parsing
@@ -16,7 +16,7 @@ func TestLocation_UnmarshalJSON(t *testing.T) {
want *Location
wantErr bool
}{
{"should unmarshal to location", []byte(`{"subadmin-area": "Paris"}`), &Location{Department: "Paris"}, false},
{"should unmarshal to location", []byte(`{"subadmin-area": "Paris"}`), &Location{SubAdminArea: "Paris"}, false},
{"should unmarshal to simple string", []byte(`"Paris"`), &Location{Simple: "Paris"}, false},
}
for _, tt := range tests {

0 comments on commit 62b7a13

Please sign in to comment.
You can’t perform that action at this time.