Setting up your development environment

We encourage you to use an IDE to write Java code. We provide a skeleton gradle project which you can use to setup your development environment.

Clone this github repo. This is a gradle project. You may need to modify build.gradle file to make it work with your favourite IDE.

If you would like to not use the above project & create your own from scratch, then you will find the below information useful:


1) http://104.155.191.79:8081/nexus/content/repositories/thirdparty is the URL of our custom mavel repository which hosts our custom code jar.

Group id: com.altin Artifact id: custom-code Version: 1.0.4

2) We also use the following dependencies (represented in gradle format):

compile("com.amazonaws:aws-lambda-java-core:1.1.0")
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.0.1'
compile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0.1'
compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.14'
compile group: 'org.glassfish.jersey.media', name: 'jersey-media-json-jackson', version: '2.19'
compile 'org.glassfish.jersey.core:jersey-client:2.23.1'

Introduction

The skeleton Java code is below.

Please note that class name & package name has to be MorphCustomCode & com.altin.custom_code. They cannot be changed.

package com.altin.custom_code;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import morph.base.beans.CustomCodeResponse;


import java.util.Map;


public class MorphCustomCode implements RequestHandler<Map<String, Object>, CustomCodeResponse> {

    @Override
    public CustomCodeResponse handleRequest(Map<String, Object> input, Context context) {

    }
}

input contains the customer and user scope variables. Customer scope variables are present in "userVariables" key and Conversation scope variables are present in "flowVariables" key.

A sample input JSON looks like:

{
  "userVariables": {
    "city": "New York",
    "age": 17,
    "orders": [
      "#o-111",
      "#o-122"
    ]
  },
  "flowVariables": {
    "booking date": "1st July 2017"
  }
}

Do not worry about the context argument.

CustomCodeResponse

Let's see what's there in the response that we need to return.

public class CustomCodeResponse {
    private List<Action> actions;

    public CustomCodeResponse() {
    }

    public List<Action> getActions() {
        return this.actions;
    }

    public void setActions(List<Action> actions) {
        this.actions = actions;
    }

    public String toString() {
        return "CustomCodeResponse{actions=" + this.actions + '}';
    }
}

Above is the source code of the bean that we need to return. Simple enough. The most important thing is the Action bean. Lets look at it.

Action

There are three types of Actions:

Action Bean Description
GoToFlowAction This is jump module of UI. It redirects to a conversation.
PublishMessageAction Used to send a message back to user.
SetVariableAction This action is used to set an attribute value. The attribute can either be of FLOW or USER scope.

The usage of these actions is described in examples below.

Examples

1) Extract a value of an attribute and setting some attributes

In the below code we are reading the phone number of the user & checking if it exists in our database. If it does then we are setting some of the attributes. If it does not then we are taking the user to another conversation named "PHONE_DOES_NOT_EXIST".

package com.altin.custom_code;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import jersey.repackaged.com.google.common.collect.Lists;
import jersey.repackaged.com.google.common.collect.Maps;
import morph.base.actions.Action;
import morph.base.actions.StringVariable;
import morph.base.actions.VariableScope;
import morph.base.actions.impl.GoToFlowAction;
import morph.base.actions.impl.SetVariableAction;
import morph.base.beans.CustomCodeResponse;

import java.util.List;
import java.util.Map;

/**
 * @author ishanjain
 * @since 18/07/17
 */
public class MorphCustomCode implements RequestHandler<Map<String, Object>, CustomCodeResponse> {


    private static Map<String, NumberDetails> numberVsDetails = Maps.newHashMap();

    static {
        List<NumberDetails> numbers = Lists.newArrayList();
        numbers.add(new NumberDetails("9711xxx400", "Sumit Mehra", "Apple", "iPhone 6S", null));
        numbers.add(new NumberDetails("8130xxx599", "Rahul Sachar", "Xiaomi", "Redmi 4", null));

        for (NumberDetails number : numbers) {
            numberVsDetails.put(number.number, number);
        }

    }

    @Override
    public CustomCodeResponse handleRequest(Map<String, Object> input, Context context) {
        /**
         * This is how you read variable. If can be userVariables or flowVariables, depending on the scope of the variable
         */
        Map<String, Object> userVariables = (Map<String, Object>) input.get("userVariables");
        /**
         * Read the variable, using the name of the variable
         */
        String phoneNumber = (String) userVariables.get("_PHONE_NUMBER");
        List<Action> actions = Lists.newArrayList();
        if (numberVsDetails.get(phoneNumber) != null) {
            NumberDetails numberDetails = numberVsDetails.get(phoneNumber);
            /**
             * Setting a variable
             */
            actions.add(new SetVariableAction("NAME", VariableScope.FLOW, new StringVariable().value(
                    numberDetails.name)));
            actions.add(new SetVariableAction("COMPANY", VariableScope.FLOW, new StringVariable().value(
                    numberDetails.handsetCompany)));
            actions.add(new SetVariableAction("MODEL", VariableScope.FLOW, new StringVariable().value(
                    numberDetails.handsetModel)));
            CustomCodeResponse customCodeResponse = new CustomCodeResponse();
            customCodeResponse.setActions(actions);
            return customCodeResponse;
        }
        CustomCodeResponse customCodeResponse = new CustomCodeResponse();
        GoToFlowAction e = new GoToFlowAction(null);
        e.setNextFlowTitle("PHONE_DOES_NOT_EXIST");
        actions.add(e);
        return customCodeResponse;
    }


    private static class NumberDetails {
        private String number;
        private String name;
        private String handsetCompany;
        private String handsetModel;
        private String link;

        public NumberDetails(String number, String name, String handsetCompany, String handsetModel, String link) {
            this.number = number;
            this.name = name;
            this.handsetCompany = handsetCompany;
            this.handsetModel = handsetModel;
            this.link = link;
        }
    }
}

2) Sending a message to the user

Let's see how we can reply back to the user. There are various types of messages that you can send. They are:

1) statement (Text message with optional buttons) 2) carousel (A horizontal carousal message) 3) list (A vertical carousal message) 4) media (Image/Video message)

All of these messages can contain suggestions (Quick Replies).

suggestions (Quick Replies)

Below code will demonstrate how to add suggestions to any kind of message. The code assumes statement (text) message.

        TextMessagePayload payload = new TextMessagePayload();
        payload.setText(
                "Thank you for booking the appointment. Please click on the button below to pay appointment fees.");
        Button button = new Button();
        button.setTitle("Pay");
        button.setButtonType(Button.ButtonType.URL);
        button.setUrl(url);
        button.setWebviewHeightRatio(Button.WebviewHeightRatio.TALL);
        payload.setButtons(Collections.singletonList(button));

        ArrayList<SuggestionElement> suggestionElements = Lists.newArrayList();
        SuggestionElement suggestion = new SuggestionElement();
        suggestion.setImageUrl("http://image-url.com");
        suggestion.setTitle("Back");
        suggestion.setPayload("back");
        suggestionElements.add(suggestion);
        payload.setSuggestionElements(suggestionElements);

message object

Message object has different attributes based on the message type. The type of the message can be one of the following:

statement

Represents a text message

Property Description Required
type value=statement Y
text The text to publish Y
buttons An array of Button objects N
suggestionElements An array of Suggestion Object N

Example code:

        TextMessagePayload payload = new TextMessagePayload();
        payload.setText(
                "Thank you for booking the appointment. Please click on the button below to pay appointment fees.");
        Button button = new Button();
        button.setTitle("Pay");
        button.setButtonType(Button.ButtonType.URL);
        button.setUrl("http://www.google.com");
        button.setWebviewHeightRatio(Button.WebviewHeightRatio.TALL);
        payload.setButtons(Collections.singletonList(button));
Property Description Required
type value=carousel Y
carousalElements An array of CarousalElement objects Y
suggestionElements An array of Suggestion Object N

Example object:

        //Making a new Payload
        CarousalMessagePayload carousalMessagePayload = new CarousalMessagePayload();
        publishMessageAction.setSimplifiedMessage(simplifiedMessage);

        //List of carousal elements. Each element is a card.
        List<Element> elements = new ArrayList<Element>();

        //Making one card
        Element element = new Element();
        element.setImageUrl("www.imageUrl.com");
        element.setTitle("Title");
        element.setSubtitle("Subtitle");

        //Adding a button
        ArrayList<Button> buttons = new ArrayList<Button>();
        Button button = new Button();
        button.setTitle("Button");
        button.setButtonType(Button.ButtonType.URL);
        buttons.add(button);
        element.setButtons(buttons);
        button.setUrl("http://morph.ai");

        elements.add(element);
list
Property Description Required
type value=list Y
carousalElements An array of CarousalElement objects Y
suggestionElements An array of Suggestion Object N

It is similar to carousal code. Use ListMessagePayload instead of CarousalMessagePayload. Rest is same.

media
Property Description Required
type value=media Y
mediaUrl The public URL of the media Y
mediaType Either "image" or "video" Y

Example code:

        MediaMessagePayload mediaMessagePayload = new MediaMessagePayload();
        //can be image or video
        mediaMessagePayload.setMediaType("image");
        mediaMessagePayload.setMediaUrl("http://morph.ai/logo.png");

3) Making an API call

Example code:

package com.altin.custom_code;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import morph.base.beans.CustomCodeResponse;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.jackson.JacksonFeature;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.Map;

/**
 * @author ishanjain
 * @since 07/08/17
 */
public class MorphCustomCode implements RequestHandler<Map<String, Object>, CustomCodeResponse> {



    @Override
    public CustomCodeResponse handleRequest(Map<String, Object> input, Context context) {
        Client client = ClientBuilder.newBuilder()
                .withConfig(new ClientConfig().property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true))
                .register(JacksonFeature.class).build();

        WebTarget target = client.target("http://you-api-call-url.com");
        Invocation.Builder request = target.request();
        Response response = request.get();

        String responseStr = response.readEntity(String.class);
        return null;
    }
}

FAQs

How to parse JSON response

You can use Jackson to parse the JSON API response.

An example:

Lets assume your endpoint is http://you-api-call-url.com

The response that it sends is in the following format:

{
  "customerId": "abc",
  "customerInfo": {
    "name": "Jane",
    "lastName": "Doe"
  }
}

There are 2 ways you can parse this JSON using Jackson.

You can either create a class & convert the JSON response to that class's object or you can convert it to JsonNode and read relevant things from the response. You can find code for both ways below.

Using JsonNode


package com.altin.custom_code;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import morph.base.beans.CustomCodeResponse;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.jackson.JacksonFeature;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Map;

/**
 * @author ishanjain
 * @since 07/08/17
 */
public class MorphCustomCode implements RequestHandler<Map<String, Object>, CustomCodeResponse> {
    @Override
    public CustomCodeResponse handleRequest(Map<String, Object> input, Context context) {
        Client client = ClientBuilder.newBuilder()
                .withConfig(new ClientConfig().property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true))
                .register(JacksonFeature.class).build();

        WebTarget target = client.target("http://you-api-call-url.com");
        Invocation.Builder request = target.request();
        Response response = request.get();

        String responseStr = response.readEntity(String.class);
        try {
            JsonNode jsonNode = new ObjectMapper().readTree(responseStr);
            String customerId = jsonNode.get("customerId").asText();
            JsonNode customerInfo = jsonNode.get("customerInfo");
            String customerName = customerInfo.get("name").asText();
            String lastName = customerInfo.get("lastName").asText();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Create a new class

package com.altin.custom_code;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import morph.base.beans.CustomCodeResponse;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.jackson.JacksonFeature;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.Map;

/**
 * @author ishanjain
 * @since 07/08/17
 */
public class MorphCustomCode implements RequestHandler<Map<String, Object>, CustomCodeResponse> {
    @Override
    public CustomCodeResponse handleRequest(Map<String, Object> input, Context context) {
        Client client = ClientBuilder.newBuilder()
                .withConfig(new ClientConfig().property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true))
                .register(JacksonFeature.class).build();

        WebTarget target = client.target("http://you-api-call-url.com");
        Invocation.Builder request = target.request();
        Response response = request.get();

        Customer customer = response.readEntity(Customer.class);

        String customerId = customer.getCustomerId();
        Customer.CustomerInfo customerInfo = customer.getCustomerInfo();
        return null;
    }

    private static class Customer {

        private String customerId;

        private CustomerInfo customerInfo;

        public String getCustomerId() {
            return customerId;
        }

        public void setCustomerId(String customerId) {
            this.customerId = customerId;
        }

        public CustomerInfo getCustomerInfo() {
            return customerInfo;
        }

        public void setCustomerInfo(CustomerInfo customerInfo) {
            this.customerInfo = customerInfo;
        }

        private static class CustomerInfo {
            private String name;
            private String lastName;

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public String getLastName() {
                return lastName;
            }

            public void setLastName(String lastName) {
                this.lastName = lastName;
            }
        }
    }
}

results matching ""

    No results matching ""