GitHub App to Build Form using Typeform and Probot

GitHub is a cloud-based software development website that uses Git as version control. Github is the brand ambassador of open source software development and gives developers the ability to collaborate on software development and host the source code as well. The code is hosted as the content of a repository. As the scope for Github increases, the Github apps have helped to reduce the complexity of software development, maintenance, and management.

Probot is a framework that allows to build Github Apps. It has a rich method library that can implement any github event response. Typeform helps to build engaging forms. Typeform also has an API platform as well, for programmatic form creation, deletion, and manipulation.

In this article, we will use Probot to build a Github app and integrate it with a typeform.
Optional software to be used: Postman(to experiment and work with Typeform API)
Framework used: Probot
We’ll also use the probot-commands and request npm module to input the command.

Initial setup of Github app

To setup the github app, one can go through the probot’s documentation. As setup is not the focus of this article, we’ll remix a Probot app on Glitch. The link for this setup can be found here.
The README.md in the Glitch remix has a fantastic guide on setting up the app.

To check for the proper app set up, run the default code that got supplied with the app install by making an issue on the repo(presuming the app is installed on the repo).
glitch-init-setup-1



Output: The bot(github app) should write a comment like below.
issue opened event leads to the bots commenting on the thread.

If this is not the output, it means that the app is not properly installed and one should consult the docs here.

After the app setup is complete and initial tests are done(to ensure the app is configured properly and working), its time to install the npm modules that we’ll need.

Install dependencies/modules

We’ll need probot-commands and request module for this app. To install them, go to ‘package.json’ file from the navigation pane on the left side. On the top-left corner of the editor, find a button labeled ‘Add Package‘. Using this button, add ‘probot-commands‘ and ‘request‘ to the app.
After these two modules are added, go to ‘index.js’ file(using the navigation pane in the left side) and add

filter_none

edit
close

play_arrow

link
brightness_4
code

var request = require("request");
const commands = require("probot-commands");

chevron_right


on top of index.js file.

dependencies-added-to-indexjs-file

Setting up Typeform

We’ll use Typeform for this app. We’ll need a ‘Personal Access Token‘ to use Typeform in our app.

  1. First, go to Typeform and sign-up/login.
  2. Now, go to Settings and then to ‘Personal Access Tokens’ under Profile section.
  3. Alternatively, directly click here and set up the tokens.
  4. The token that you generate should be copied and stored somewhere for persistence. Use this token and add it to ‘.env’ file in the Glitch app

env-setup-for-typeform



Now that app is working, probot-commands and request modules are set up, and typeform is properly integrated, we’ll have a look at the code.

Code:
Find the index.js file here(please note that these are code snippets and exactly copy-pasting this code might not work).

For this demo, to set up the ‘request'(that needs to be made to the Typeform servers to create the form), Postman(the software) is used. Besides this, this reference is used to create the form.

looking at the code(consult the index.js from here):

filter_none

edit
close

play_arrow

link
brightness_4
code

// These two lines 'require' the modules to the app.
var request = require("request");
const commands = require("probot-commands"); 
  
module.exports = app => {
 // all the future code goes here
   
}; 
// this acts as a kind of wrapper 
// in which all the code that runs the
// function(adding labels, creating forms) of 
// the app goes.
  
app.on(["issue_comment.created", "issues.opened"], async context =>
    // all the future code goes here
    // this block comes inside the 'module.exports' 
    // block described above
    // app.on executes code in response to events on github.
    // these events are connected to webhooks which trigger these 
    // app.on functions
    // this code executes in response to when a 'issue comment 
    // is created' or when a 'new issue is opened'. They can be 
    // listened to individually as well, by using two 
    // separate 'app.on' listeners.
    // the 'context' contains all the details related to 
    // the event that fired on github
});
  
if (context.payload.comment.user.type === "Bot"
{
   context.log("comment user type is bot, returning..");
   return;
  
// this code block checks to see if the comment is 
// made by a Bot or not. If the comment is made by a bot, the 
// function returns(to prevent bot from running on its own 
// comments, and going into some kind of a 
//(potentially)loop)
  
var createform;
if ((context.payload.issue.author_association === "OWNER") ||
    (context.payload.issue.author_association === "COLLABORATOR")) 
{
 // stuff related to the form goes here
  
// we check for the appropriate credentials here. If the user
// is the 'OWNER'(of the repo/org) or the 'COLLABORATOR', then 
// only proceed towards making a form..
  
// inside the 'stuff related to the form goes here'
commands(app, "createform", (context, command) =>
{
    context.log("entered createform commands");
    createform = command.arguments.split(/[\s] */);
    var createformlength = createform.length;
    context.log(createform);
   
// ...more code here
}
  
// in this code snippet, we listen for a createform 
// 'slash command', as it is called. check the below 
// image to see what a command like this looks.
   
// to process this 'slash command', probot-commands module is used.
// explanation: context.log() //acts as a logging tactic
// command.arguments.split split the 'slash command' 
// into individual values and put them in an array. 
// [\s] helps to // split the command on a 'space'. 
// For example, in the image above, we obtain as output of 
// context.log(createform): ['formtitle1', 'field1title', 
// 'multiple_choice', 'label1', 'label2']

chevron_right


slash command github snip

filter_none

edit
close

play_arrow

link
brightness_4
code

// the form code block. Find the explanation for 
// each snippet as the corresponding comment. 
// This bit of code is derived from the Postman.
{
    var options = {
        method: "POST"
          
        // POST method to create 
        // the form, according to typeform api
        url: "https://api.typeform.com/forms", //url
        headers: {
            Accept: "application/json",
            "Content-Type": [
                "application/x-www-form-urlencoded",
                "application/json"
            ],
            Host: "api.typeform.com",
            Authorization: `${process.env.TYPEFORM_KEY}`, 
              
            // here is the personal access token generated earlier
            Cookie: "device_view=full"
        },
        // to know more details about below code field, 
        body: JSON.stringify({
            title: createform[0], 
              
            // value = "formtitle" (consult the image 
            // above of github comment)
            settings: {
                language: "en", // language of form
                is_public: true, // so that everyone can see and respond
                progress_bar: "percentage",
                show_progress_bar: true
            },
            welcome_screens: [{
                ref: "nice-readable-welcome-ref"
                  
                // used to uniquely refer
                // the welcome_screens field
                title: "Welcome", // title of welcome screen
                properties: {
                    description: 'description ' + context.
                    payload.comment.url,
                      
                    // links back to github issue 
                    // comment to get more knowledge about 
                    // the reason to create this form. link to the 
                    // comment that created this form
                    show_button: true,
                    button_text: "start"
                }
            }],
            thankyou_screens: [{
                ref: "nice-readable-thank-you-ref",
                title: "Thank you", // final screen
                properties: {
                    show_button: true,
                    button_text: "start",
                    button_mode: "redirect",
                    redirect_url: "https://www.typeform.com",
                    share_icons: false
                }
            }],
            fields: [
                  
            // actual field that will record response. 
            // is an array of objects, each obj represents 1 field
                {
                ref: "field1"
                  
                // to uniquely identify 
                // first field(for programmatically manipulation)
                title: createform[1], 
                  
                // value = "field1title" 
                // (consult the image above of github comment)
                type: createform[2], 
                  
                // value = multiple-choice. 
                // sets the type of question
                properties: {
                    description: "field1 desc",
                      
                    // to provide more info about field
                    randomize: true,
                    allow_multiple_selection: true,
                    allow_other_choice: true,
                    vertical_alignment: false,
                    choices: [
                        { label: createform[3], ref: "field1_label1_ref" }, 
                        // label1, value = label1
                          
                        { label: createform[4], ref: "field1_label2_ref"
                        // label2, value = label2
                    ]
                }
            }, ]
        })
    };
    request(options, function(error, response) 
    
        // send the request to typeform servers to create form
        if (error) 
        {
            const params1 = context.issue({ 
            body: 'form not made. error processing command' 
            }
              
)           // if error, comment
            return context.github.issues.createComment(params1);
              
            // if error, comment
        };
        console.log(response.body);
        const params2 = context.issue(
        
            body: response.body._links.value }); 
              
            // if success, comment the link to form
        return context.github.issues.createComment(params2);
        // if success, comment the link to form
    });
}

chevron_right


The form gets built.
form-created-log

Future scope/activity for you: