Building user interfaces

Our objective is to build a UI where users can key in a long url and get a shortened bitly URL.

Installation Page

Open app/index.html

<!DOCTYPE html>

<html lang="en">
  <head>
    <title>A Template App</title>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script async src="{{{appclient}}}"></script>
    <script
      async
      type="module"
      src="https://cdn.jsdelivr.net/npm/@freshworks/crayons@v4/dist/crayons/crayons.esm.js"
    ></script>
    <script
      async
      nomodule
      src="https://cdn.jsdelivr.net/npm/@freshworks/crayons@v4/dist/crayons/crayons.js"
    ></script>

    <link rel="stylesheet" type="text/css" href="styles/style.css" />
  </head>

  <body>
    <script type="module">
      import { createApp } from 'https://unpkg.com/petite-vue?module';
      var client;
      (async function () {
        client = await app.initialized();
      })();

      function Shorten() {
        return {
          $template: '#shorten-space',
          long_url: '',
          short_url: '',
          last5Urls: [],
          async storeUrls() {
            try {
              let index = 0;
              await client.db.set('last5Urls', {
                index,
                short_url: this.short_url,
              });
              index++;
              return;
            } catch (error) {
              console.error('Storing into platform KV store failed', error);
            }
          },
          async listUrls() {
            try {
              let res = await client.db.get('last5Urls');
              this.last5Urls.push(res);
              console.log(this.last5Urls);
            } catch (error) {
              console.error(
                'Retrieval from the platform KV store failed',
                error,
              );
            }
          },
          async shortenUrl() {
            this.short_url = 'shortening using bitly...';

            try {
              let { response } = await client.request.invokeTemplate(
                'shortenLink',
                {
                  body: JSON.stringify({
                    long_url: this.long_url,
                    cache: true,
                  }),
                },
              );
              let shortenedObj = JSON.parse(response);
              this.short_url = shortenedObj.link;
              this.storeUrls();
            } catch (error) {
              this.short_url = 'shortening failed...';
              console.error('this went wrong', error);
            }
          },
        };
      }

      createApp({
        Shorten,
      }).mount();
    </script>

    <template id="shorten-space">
      <div class="main">
        <div class="inputbox">
          <p>Enter URL to shorten</p>
          <fw-input
            type="url"
            required="true"
            name="long-url"
            v-model="long_url"
            placeholder="https://www.example.com"
            aria-label="long url text"
          ></fw-input>
          <fw-button @click="shortenUrl()" color="secondary" size="normal"
            >Shrink</fw-button
          >
          <fw-button @click="listUrls()" color="link" size="normal"
            >List last 5 shortened URLs</fw-button
          >
        </div>
        <pre>{{short_url}}</pre>
        <ul class="url-list">
          <li v-for="item in last5Urls" :key="item.index">
            {{item.short_url}}
          </li>
        </ul>
      </div>
      <style>
        .inputbox {
          display: flex;
          flex-flow: row wrap;
          justify-content: space-around;
        }

        fw-input {
          width: 100%;
          padding: 0.5rem;
        }

        fw-button {
          width: 75%;
        }

        li {
          list-style: none;
          font-family: 'Courier New';
          color: #0077cc;
        }

        li:hover {
          text-decoration: underline;
        }
      </style>
    </template>

    <div v-scope="Shorten()"></div>
  </body>

  <style>
    html {
      box-sizing: border-box;
      font-family: Arial, Helvetica, sans-serif;
    }

    *,
    *:before,
    *:after {
      box-sizing: inherit;
    }
  </style>
</html>

In this tutorial, we will want to focus and spend more time on the modular aspects of the global apps you will build. So, let’s briefly understand what’s going on this index.html page

  1. The appclient is automatically substituted with a platform library to allow our app to interact with host UI - Freshdesk properties or react to UI events.
  2. The JS logic is not separated since logic is less, and petite vue facilitates the Javascript runtime in fewer lines.
  3. We have CSS Box Sizing implemented.
  4. The URLs are also persisted into DB using the client.db calls.

Configuring Requests

We will have to make API calls to Bitly to shorten the URLs. Our platforms provides developers with Request Templates to make those API requests

Open config/requests.json

1{
2  "shortenLink": {
3    "schema": {
4      "method": "POST",
5      "host": "api-ssl.bitly.com",
6      "path": "/v4/shorten",
7      "headers": {
8        "Authorization": "Bearer <%= iparam.apiKey %>",
9        "Content-Type": "application/json"
10      }
11    }
12  }
13}

Here’s a quick breakdown:

  1. shortenLink: The name of the API request configuration.
  2. schema: The object containing the details of the API request.
    1. method: The HTTP method used for the request, which is POST in this case.
    2. host: The base URL of the API, which is api-ssl.bitly.com.
    3. path: The specific API endpoint path, which is /v4/shorten for shortening a link using Bitly's API.
    4. headers: The object containing the headers to be sent with the request.
    5. Authorization: The header for passing the API key for authentication. It uses the Bearer scheme and retrieves the API key from the app's installation parameters (iparams) using &lt;%= iparam.apiKey %>.
    6. Content-Type: The header specifying the content type of the request body, which is application/json.

When this configuration is used in a Freshworks app, it will make a POST request to the Bitly API at https://api-ssl.bitly.com/v4/shorten with the specified headers to shorten a link.