From 39084f69c459387d30122faf909d591df017026d Mon Sep 17 00:00:00 2001 From: MishUshakov Date: Tue, 30 Jul 2019 12:12:43 +0200 Subject: [PATCH] New build --- Dockerfile | 4 +- LICENSE | 2 +- README.md | 73 +++++---- main.go | 76 ++++++---- test.http | 5 + ui/assets/card.svg | 1 - ui/assets/send.svg | 1 - ui/index.html | 362 --------------------------------------------- 8 files changed, 101 insertions(+), 423 deletions(-) delete mode 100644 ui/assets/card.svg delete mode 100644 ui/assets/send.svg delete mode 100644 ui/index.html diff --git a/Dockerfile b/Dockerfile index efe4b0a..8e9d5d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,3 @@ FROM scratch COPY bin/fulfillment-tester-linux / -COPY ui /ui -VOLUME ["/tmp"] -ENTRYPOINT ["/fulfillment-tester-linux"] \ No newline at end of file +ENTRYPOINT /fulfillment-tester-linux \ No newline at end of file diff --git a/LICENSE b/LICENSE index a56f8d8..5408b5c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 Ushakov +Copyright 2019 by Mikhail Ushakov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index d1b7627..7c92a47 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,81 @@ -# Dialogflow Fulfillment Tester +# Fulfillment Tester -Test your Fulfillments like never before! +Test your Dialogflow/Actions on Google Fulfillments like never before! -Start it: +- Works on Windows, Linux and Mac (64 bit) +- Small, embeddable binary with no external dependencies, written in Go (<10 MB) +- Supports any dialogflow fulfillment in any programming language, platform-specific responses and Actions on Google +- No More waiting. Never. Run your fulfillment (locally or remotely) and start testing +- Run automated tests with tools like Jest, Ava, Mocha (or any other of your choice) +- Test with a built-in User Interface or on CI/CD +- Acts exactly like Dialogflow, 100% accurate testing results guaranteed -![](https://i.imgur.com/Y5Ufr32.png) +Just start it: -And test with style using the User Interface: +![](https://i.imgur.com/yE2aiid.png) -![](https://i.imgur.com/YSulY94.png) +And test with style using [Dialogflow for Web](https://github.com/mishushakov/fulfillment-tester): -Or send the requests manually: +![](https://i.imgur.com/sZR5c63.png) +![](https://i.imgur.com/VmPCd0u.png) +![](https://i.imgur.com/BWbropG.png) + +Or send requests manually (programmatically): ![](https://i.imgur.com/ywbIjrE.jpg) -## Features +## How it works -- Works on Windows, Linux and Mac (64 bit) -- Small binary with no external dependencies, written in Go (<10 MB) -- Test fulfillments in any programming language and Actions on Google fulfillments -- No More waiting. Never. Just run your fulfillment (locally or remotely) and start testing -- Run automated tests with tools like Jest, Ava, Mocha (or any other of your choice) -- Test with convinience, with a built-in User Interface or on CI/CD -- Works exactly like Dialogflow, 100% accurate testing results guaranteed +![](https://svgur.com/i/EKw.svg) -Excited? Let's get started! +Fulfillment tester acts like a reverse-proxy between Dialogflow and your fulfillment. It fetches Dialogflow response for a given query, forwards it to the specified fulfillment and then responds with a result. ## Installation -Connect your Dialogflow Agent to [Dialogflow Gateway](https://dialogflow.cloud.ushakov.co), read the guide [here](https://github.com/mishushakov/dialogflow-gateway-docs/blob/master/guide.md) +1. Install Dialogflow Gateway first. Fulfillment tester uses Dialogflow Gateway as its backend. + + Dialogflow Gateway enables third-party integrations to securely access the Dialogflow V2 API + + - [Documentation](https://github.com/mishushakov/dialogflow-gateway-docs) + - [Implementations](https://github.com/mishushakov/dialogflow-gateway-docs#implementations) -Install the latest executable for your operating system from the [Releases Page](https://github.com/mishushakov/dialogflow-fulfillment-tester/releases) +2. Install the latest executable for your operating system from the [Releases Page](https://github.com/mishushakov/fulfillment-tester/releases) Run ```sh -dialogflow-fulfillment-tester --project --fulfillment +fulfillment-tester --gateway --fulfillment ``` Get help: ```sh -dialogflow-fulfillment-tester --help +fulfillment-tester --help ``` Tip: if you are on node and firebase functions, run your function locally using the firebase functions emulator -## Installing the UI +## Accessing the UI -If you want the UI, clone this repo and put the `ui` folder near the executable. +1. Follow the installation instructions in the [Dialogflow for Web repo](https://github.com/mishushakov/dialogflow-web-v2) +2. Change the Gateway URL in the `config.js` to match your fulfillment tester URL -Notice: when running Dialogflow Fulfillment Tester on a diffrent host/port, make sure to change it in the UI as well (`index.html`): - -```js -let url = "http://localhost:8899" // <- Change the url, when running on a different host/port -``` + Example: -Notice: the UI doesn't display Actions on Google components at the moment. + ```js + export default { + app: { + gateway: "http://localhost:8899" + [...] + } + } -Tip: When inspecting using the UI, open the console to see the request/response body + [...] + ``` ## Making Requests -The request/response format of the Dialogflow Fulfillment Tester is equal to the Dialogflow Gateway request/response format, which is equal to the Dialogflow request/response format. Read the docs [here](https://github.com/mishushakov/dialogflow-gateway-docs/blob/master/api.md) +The request/response format of Fulfillment Tester is equal to the [Dialogflow Gateway API](https://github.com/mishushakov/dialogflow-gateway-docs#api) request/response format ## Building from source diff --git a/main.go b/main.go index bed3f55..20c5bcc 100644 --- a/main.go +++ b/main.go @@ -8,10 +8,10 @@ import ( "net/http" ) -// Declaring Variables +// Variables var ( - Project string // Project ID - Fulfillment string // Fulfillment Host + Gateway string // Gateway URL + Fulfillment string // Fulfillment URL Port string // Port to run the fulfillment-tester at ) @@ -21,45 +21,74 @@ func proxy(res http.ResponseWriter, req *http.Request) { res.Header().Set("Access-Control-Allow-Headers", "Content-Type, Cache-Control") res.Header().Set("Access-Control-Allow-Methods", "*") - if req.Method == "POST" { + if req.Method == "GET" { + // Retrieve Agent + resp, gateway_conn_err := http.Get(Gateway) + if gateway_conn_err != nil { + panic(gateway_conn_err) + } + body, gateway_res_err := ioutil.ReadAll(resp.Body) + if gateway_res_err != nil { + panic(gateway_res_err) + } + + res.Write(body) + } else if req.Method == "POST" { // Proxy request to Dialogflow Gateway (Session ID is always fulfillment-tester) - resp, _ := http.Post("https://"+Project+".gateway.dialogflow.cloud.ushakov.co/fulfillment-tester", "application/json", req.Body) + resp, gateway_conn_err := http.Post(Gateway+"/fulfillment-tester", "application/json", req.Body) + if gateway_conn_err != nil { + panic(gateway_conn_err) + } if resp.StatusCode != 200 { - res.WriteHeader(403) + res.WriteHeader(resp.StatusCode) } // Read the response from Dialogflow Gateway - // Add session identifier to response - // Generate new JSON - resp_body, _ := ioutil.ReadAll(resp.Body) + resp_body, gateway_res_err := ioutil.ReadAll(resp.Body) + if gateway_res_err != nil { + panic(gateway_res_err) + } + var result map[string]interface{} json.Unmarshal(resp_body, &result) - result["session"] = "projects/" + Project + "/agent/sessions/fulfillment-tester" + + // Add (fake) session identifier to response, so our Webhook can see the session + // Generate new json + result["session"] = "projects/x/agent/sessions/fulfillment-tester" data, _ := json.Marshal(result) - // Send the response to Fulfillment - fulfillment_resp, _ := http.Post(Fulfillment, "application/json", bytes.NewReader(data)) + // Send it to the Fulfillment + fulfillment_resp, fulfillment_conn_err := http.Post(Fulfillment, "application/json", bytes.NewReader(data)) + if fulfillment_conn_err != nil { + panic(fulfillment_conn_err) + } - // Parse the fulfillment_messages from the Fulfillment response - // Overwrite queryResult on response (addr is the address of the queryResult field, that can be pointed to later) + // Parse response from fulfillment + // Set queryResult of Dialogflow Gateway response to the fulfillment's response // That's how Dialogflow actually responds with fulfillment option enabled (you don't have to be a Googler to tell that) - fulfillment_messages, _ := ioutil.ReadAll(fulfillment_resp.Body) - var addr = result["queryResult"] - json.Unmarshal(fulfillment_messages, &addr) + fulfillment_messages, fulfillment_res_err := ioutil.ReadAll(fulfillment_resp.Body) + if fulfillment_res_err != nil { + panic(fulfillment_res_err) + } + + var queryResult = result["queryResult"] + json.Unmarshal(fulfillment_messages, &queryResult) - // Convert output back to JSON and send it to the client + // Convert result back to JSON and send it to the client output, _ := json.Marshal(result) res.Write(output) } else if req.Method == "OPTIONS" { + // Pre-flight checks res.WriteHeader(200) } else { + // Invalid request method res.WriteHeader(404) } } func main() { // Parse flags, setup default flags and descriptions - flag.StringVar(&Project, "project", "dialogflow-web-v2", "Dialogflow Gateway Project ID") + flag.StringVar(&Gateway, "gateway", "https://dialogflow-web-v2.gateway.dialogflow.cloud.ushakov.co", "Dialogflow Gateway URL") flag.StringVar(&Fulfillment, "fulfillment", "https://us-central1-dialogflow-web-v2.cloudfunctions.net/dialogflowFirebaseFulfillment", "URL to fullfillment (remote or local)") flag.StringVar(&Port, "port", "8899", "Port to run the fulfillment-tester at") flag.Parse() @@ -67,15 +96,12 @@ func main() { // Log some useful information to console println("Dialogflow Fulfillment Tester is running 🚀") println("Listening on: http://localhost:" + Port) - println("Web UI at: http://localhost:" + Port + "/ui" + "\n") - println("Connection 🔌") - println("Project ID: " + Project) - println("Fulfillment URL: " + Fulfillment) + println("\nConnection 🔌") + println("Gateway: " + Gateway) + println("Fulfillment: " + Fulfillment) println("\nHappy Testing!") // Setup HTTP Server and Proxy (Handler) - // Setup the Web Server for the Web Client http.HandleFunc("/", proxy) - http.Handle("/ui/", http.StripPrefix("/ui", http.FileServer(http.Dir("ui")))) panic(http.ListenAndServe(":"+Port, nil)) } \ No newline at end of file diff --git a/test.http b/test.http index 7eebc96..90257e0 100644 --- a/test.http +++ b/test.http @@ -1,3 +1,8 @@ +### Get Agent +GET http://localhost:8899 +Content-Type: application/json + +### Detect Intent POST http://localhost:8899 Content-Type: application/json diff --git a/ui/assets/card.svg b/ui/assets/card.svg deleted file mode 100644 index 738cbc9..0000000 --- a/ui/assets/card.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/assets/send.svg b/ui/assets/send.svg deleted file mode 100644 index 9bdf1ed..0000000 --- a/ui/assets/send.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/index.html b/ui/index.html deleted file mode 100644 index ad7a345..0000000 --- a/ui/index.html +++ /dev/null @@ -1,362 +0,0 @@ - - - - - Dialogflow Fulfillment Tester - - - - - - - -
- -
-
-
Dialogflow Fulfillment Tester
- -
-
- - -
- - - - - - - - -
{{message.queryResult.queryText}}
- -
{{component.text.text[0]}}
- - -
- -
-
{{component.card.title}}
-
{{component.card.subtitle}}
- -
-
- - - - - - -
-
-
- - -
-
-
-
- -
-
- -
-
-
-
-
- - - - \ No newline at end of file