Category: Engineering blogs

  • Creating a Frictionless SignUp Experience with Auth0 for your Application

    What is Auth0 and frictionless signup?

    Auth0 is a service that handles your application’s authentication and authorization needs with simple drop-in solutions. It can save time and risk compared to building your own authentication/authorization system. Auth0 even has its own universal login/signup page that can be customized through the dashboard, and it also provides APIs to create/manage users.

    A frictionless signup flow allows the user to use a core feature of the application without forcing the user to sign up first. Many companies use this flow, namely Bookmyshow, Redbus, Makemytrip, and Goibibo. 

    So, as an example, we will see how an application like Bookmyshow looks with this frictionless flow. First, let’s assume the user is a first-time user for this application; the user lands on the landing page, selects a movie, selects the theater, selects the number of seats, and then lands on the payment page where they will fill in their contact details (email and mobile number) and proceed to complete the booking flow by paying for the ticket. At this point, the user has accessed the website and made a booking without even signing up.

    Later on, when the user sign ups using the same contact details which were provided during booking, they will notice their previous bookings and other details waiting for them on the app’s account page.

    What we will be doing in this blog?

    In this blog, we will be implementing Auth0 and replicating a similar feature as mentioned above using Auth0. In this code sample, we will be using react.js for the frontend and nest.js for the backend. 

    To keep the blog short, we will only focus on the logic related to the frictionless signup with Auth0. We will not be going through other aspects, like payment service/integration, nest.js, ORM, etc.

    Setup for the Auth0 dashboard:

    Auth0’s documentation is pretty straightforward and easy to understand; we’ll link the sections for this setup, and you can easily sign up and continue your setup with the help of their documentation.

    Do note that you will have to create two applications for this flow. One is a Single-Page Application for your frontend so that you can initiate login from your frontend app and the other is ManagementV2 for your server so that you can use their management APIs to create a user.

    After registering you will get the client id and client secret on the application details page, you will require these keys to plug it in auth0’s SDK so you will be able to use its APIs in your application.

    Setup for your single-page application:

    To use Auth0’s API, we would have to install its SDK. For single-page applications, Auth0 has rewritten a new SDK from auth0-js called auth0-spa-js. But if you are using either Angular, React, or Vue, then auth0 already has created their framework/library-specific wrapper for us to use.

    So, we will move on to installing its React wrapper and continuing with the setup:

    npm install @auth0/auth0-react

    Then, we will wrap our app with Auth0Provider and provide the keys from the Auth0 application settings dashboard:

    <Auth0Provider
         domain={process.env.NEXT_PUBLIC_AUTH0_DOMAIN}
         clientId={process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID}
         redirectUri={
           typeof window !== 'undefined' &&
           `${window.location.origin}/auth-callback`
         }
         onRedirectCallback={onRedirectCallback}
         audience={process.env.NEXT_PUBLIC_AUTH0_AUDIENCE}
         //Safari uses ITP which prevents silent auth.Please refer https://www.py4u.net/discuss/353302
         useRefreshTokens={true}
         cacheLocation="localstorage"
       >
         </App>
       </Auth0Provider>

    You will find the explanation of the above props and more on Auth0’s React APIs through their GitHub link https://github.com/auth0/auth0-react.

    But we do want to cover one issue with their authenticated state and redirection. We noticed that when Auth0 redirects to our application, the isAuthenticated flag doesn’t get reflected immediately. The states get sequentially updated like so:

    • isLoading: false
      isAuthenticated: false
    • isLoading: true
      isAuthenticated: false
    • isLoading: false
      isAuthenticated: false
    • isLoading: false
      isAuthenticated: true

    This can be a pain if you have some common redirection logic based on the user‘s authentication state and user type. 

    What we found out from the Auth0’s community forum is that Auth0 does take some time to parse and update its states, and after the update operations, it then calls the onRedirectCallback function, so it’s safe to put your redirection logic in onRedirectCallback, but there is another issue with that. 

    The function doesn’t have access to Auth0’s context, so you can’t access the user object or any other state for your redirection logic, so you would want to redirect to a page where you have your redirection logic when onRedirectCallback is called.

    So, in place of the actual page set in redirectUri, you would want to use a buffer page like the /auth-callback route where it just shows a progress bar and nothing else.

    Implementation:

    For login/signup, since we are using the universal page we don’t have to do much, we just have to initiate the login with loginWithRedirect() function from the UI, and Auth0 will handle the rest.

    Now, for the core part of the blog, we will now be creating a createBooking API on our nest.js backend, which will accept email, mobile number, booking details (movie, theater location, number of seats), and try to create a booking.

    In this frictionless flow, internally the application does create a user for the booking to refer to; otherwise, it would be difficult to show the bookings once the user signups and tries to access its bookings. 

    So, the logic would go as follows: first, it will check if a user exists with the provided email in our DB. If not, then we will create the user in Auth0 through its management API with a temporary password, and then we will link the newly created Auth0 user in our users table. Then, by using this, we will create a booking.

    Here is an overview of how the createBooking API will look:

    @Post('/bookings/create')
      async createBooking(
        @Body() createBookingDto: createBookingDto
      ): Promise<BookingResponseDto> {
        const { email } = createBookingDto;
        // Checks if the email exists or not, if it doesn’t exists then we will create an account, else we will use the existing user to create a booking
        let user = await this.userRepository.findByEmail(email);
     
        if (!user){
        	const password = Utilities.generatePassword(16);
        	// We use a random password here to create the user on auth0 
        	const { auth0Response } = await this.createUserOnAuth0(
            email,
      password,
        	);
        	this.logger.debug(auth0Response, 'Created Auth0 User');
     
        	let userData: CreateUserDto = {
            email,
            auth0UserId: auth0Response['_id'],
        	};
        	// Creates and links the auth0 user with our DB
        	user = await this.userRepository.addUser(userData);
        }
     
        const booking = {
    userId: user.id,
    transactionId: createBookingDto.transaction.id, // Assuming the payment was done before this API call in a different service
    showId: createBookingDto.show.id,
    theaterId: createBookingDto.theater.id,
    seatNumbers: createBookingDto.seats
        }
        // Creates a booking 
        const bookingObject = await this.bookingRepository.bookTicket(booking)
     
        return new BookingResponseDto(bookingObject)
      }

    As for creating the user on Auth0, we will use Auth0’s management API with the /dbconnections/signup endpoint.
    Apart from the config details that the API requires (client_id, client_secret and connection), it also requires email and password. For the password, we will use a randomly generated one.

    After the user has been created, we will send a forgotten password email to that email address so that the user can set the password and access the account.

    Do note you will have to use the client_id, client_secret, and connection of the ManagementV2 application that was created in the Auth0 dashboard.

    private async createUserOnAuth0(
        email: string,
        password: string,
        createdBy: string,
        retryCount = 0,
      ): Promise<Record<string, string>> {
        try {
          const axiosResponse = await this.httpService
            .post(
              `https://${configService.getAuth0Domain()}/dbconnections/signup`,
              {
                client_id: configService.getAuth0ClientId(),
                client_secret: configService.getAuth0ClientSecret(),
                connection: configService.getAuth0Connection(),
                email,
                password,
              },
            )
            .toPromise();
     
          this.logger.log(
            axiosResponse.data.email,
            'Auth0 user created with email',
          );
     
          // Send password reset email
          this.sendPasswordResetEmail(email);
     
          return { auth0Response: axiosResponse.data, password };
        } catch (err) {
          this.logger.error(err);
          /**
           * {@link https://auth0.com/docs/connections/database/password-strength}
           * Auth0 does not send any specific response, so here we are calling create user again
           * assuming password failed to meet the requirement of auth0
           * But here also we are gonna try it ERROR_RETRY_COUNT times and after that stop call,
           * so we don't get in infinite loop
           */
          if (retryCount < ERROR_RETRY_COUNT) {
            return this.createUserOnAuth0(
              email,
              Utilities.generatePassword(16),
              createdBy,
              retryCount + 1,
            );
          }
     
          throw new HttpException(err, HttpStatus.BAD_REQUEST);
        }
      }

    To send the forgotten password email, we will use the /dbconnections/change_password endpoint from the management API. The code is pretty straightforward.

    This way, the user can change the password, and he/she will be able to access their account.

    private async sendPasswordResetEmail(email: string): Promise<void> {
        try {
          const axiosResponse = await this.httpService
            .post(
              `https://${configService.getAuth0Domain()}/dbconnections/change_password`,
              {
                client_id: configService.getAuth0ClientId(),
                client_secret: configService.getAuth0ClientSecret(),
                connection: configService.getAuth0Connection(),
                email,
              },
            )
            .toPromise();
     
          this.logger.log(email, 'Password reset email sent to');
     
          return axiosResponse.data;
        } catch (err) {
          this.logger.error(err);
        }
      }

    With this, the user can now make a booking without signing up and have a user created in Auth0 for that user, so when he/she logs in later using the universal login page, Auth0 will have a reference for it.

    Conclusion:

    Auth0 is a great platform for managing your application’s authentication and authorization needs if you have a simple enough login/signup flow. It can get a bit tricky when you are trying to implement a non-traditional login/signup flow or a custom flow, which is not supported by Auth0. In such a scenario, you would need to add some custom code as explained in the example above. 

  • UI Automation and API Testing with Cypress – A Step-by-step Guide

    These days, most web applications are driven by JavaScript frameworks that include front-end and back-end development. So, we need to have a robust QA automation framework that covers APIs as well as end-to-end tests (E2E tests). These tests check the user flow over a web application and confirm whether it meets the requirement. 

    Full-stack QA testing is critical in stabilizing APIs and UI, ensuring a high-quality product that satisfies user needs.

    To test UI and APIs independently, we can use several tools and frameworks, like Selenium, Postman, Rest-Assured, Nightwatch, Katalon Studio, and Jest, but this article will be focusing on Cypress.

    We will cover how we can do full stack QA testing using Cypress. 

    What exactly is Cypress?

    Cypress is a free, open-source, locally installed Test Runner and Dashboard Service for recording your tests. It is a frontend and backend test automation tool built for the next generation of modern web applications.

    It is useful for developers as well as QA engineers to test real-life applications developed in React.js, Angular.js, Node.js, Vue.js, and other front-end technologies.

    How does Cypress Work Functionally?

    Cypress is executed in the same run loop as your application. Behind Cypress is a Node.js server process.

    Most testing tools operate by running outside of the browser and executing remote commands across the network. Cypress does the opposite, while at the same time working outside of the browser for tasks that require higher privileges.

    Cypress takes snapshots of your application and enables you to time travel back to the state it was in when commands ran. 

    Why Use Cypress Over Other Automation Frameworks?

    Cypress is a JavaScript test automation solution for web applications.

    This all-in-one testing framework provides a chai assertion library with mocking and stubbing all without Selenium. Moreover, it supports the Mocha test framework, which can be used to develop web test automations.

    Key Features of Cypress:

    • Mocking – By mocking the server response, it has the ability to test edge cases.
    • Time Travel – It takes snapshots as your tests run, allowing users to go back and forth in time during test scenarios.
    • Flake Resistant – It automatically waits for commands and assertions before moving on.
    • Spies, Stubs, and Clocks – It can verify and control the behavior of functions, server responses, or timers.
    • Real Time Reloads – It automatically reloads whenever you make changes to your tests.
    • Consistent Results – It gives consistent and reliable tests that aren’t flaky.
    • Network Traffic Control – Easily control, stub, and test edge cases without involving your server.
    • Automatic Waiting – It automatically waits for commands and assertions without ever adding waits or sleeps to your tests. No more async hell. 
    • Screenshots and Videos – View screenshots taken automatically on failure, or videos of your entire test suite when it has run smoothly.
    • Debuggability – Readable error messages help you to debug quickly.

       


    Fig:- How Cypress works 

     

     Installation and Configuration of the Cypress Framework

    This will also create a package.json file for the test settings and project dependencies.

    The test naming convention should be test_name.spec.js 

    • To run the Cypress test, use the following command:
    $ npx cypress run --spec "cypress/integration/examples/tests/e2e_test.spec.js"

    • This is how the folder structure will look: 
    Fig:- Cypress Framework Outline

    REST API Testing Using Cypress

    It’s important to test APIs along with E2E UI tests, and it can also be helpful to stabilize APIs and prepare data to interact with third-party servers.

    Cypress provides the functionality to make an HTTP request.

    Using Cypress’s Request() method, we can validate GET, POST, PUT, and DELETE API Endpoints.

    Here are some examples: 

    describe(“Testing API Endpoints Using Cypress”, () => {
    
          it(“Test GET Request”, () => {
                cy.request(“http://localhost:3000/api/posts/1”)
                     .then((response) => {
                            expect(response.body).to.have.property('code', 200);
                })
          })
    
          it(“Test POST Request”, () => {
                cy.request({
                     method: ‘POST’,
                     url: ‘http://localhost:3000/api/posts’,
                     body: {
                         “id” : 2,
                         “title”:“Automation”
                     }
                }).then((response) => { 
                        expect(response.body).has.property(“title”,“Automation”); 
                })
          })
    
          it(“Test PUT Request”, () => {
                cy.request({
                        method: ‘PUT’,
                        url: ‘http://localhost:3000/api/posts/2’,
                        body: { 
                           “id”: 2,
                           “title” : “Test Automation”
                        }
                }).then((response) => { 
                        expect(response.body).has.property(“title”,“ Test Automation”); 
                })          
          })        
    
          it(“Test DELETE Request”, () => {
                cy.request({
                          method : ‘DELETE’,
                          url: ‘http://localhost:3000/api/post/2’
                          }).then((response) => {
                            expect(response.body).to.be.empty;
                })	
          })
       
     })

    How to Write End-to-End UI Tests Using Cypress

    With Cypress end-to-end testing, you can replicate user behaviour on your application and cross-check whether everything is working as expected. In this section, we’ll check useful ways to write E2E tests on the front-end using Cypress. 

    Here is an example of how to write E2E test in Cypress: 

    How to Pass Test Case Using Cypress

    1. Navigate to the Google website
    2. Click on the search input field 
    3. Type Cypress and press enter  
    4. The search results should contain Cypress

    How to Fail Test Case Using Cypress

    1. Navigate to the wrong URL http://locahost:8080
    2. Click on the search input field 
    3. Type Cypress and press enter
    4. The search results should contain Cypress  
    describe('Testing Google Search', () => {
    
         // To Pass the Test Case 1 
    
         it('I can search for Valid Content on Google', () => {
    
              cy.visit('https://www.google.com');
              cy.get("input[title='Search']").type('Cypress').type(‘{enter}’);
              cy.contains('https://www.cypress.io'); 
    
         });
    
         // To Fail the Test Case 2
    
         it('I can navigate to Wrong URL’', () => {
    
              cy.visit('http://localhost:8080');
              cy.get("input[title='Search']").type('Cypress').type(‘{enter}’);
              cy.contains('https://www.cypress.io'); 
    
         });
     
    });

    Cross Browser Testing Using Cypress 

    Cypress can run tests across the latest releases of multiple browsers. It currently has support for Chrome and Firefox (beta). 

    Cypress supports the following browsers:

    • Google Chrome
    • Firefox (beta)
    • Chromium
    • Edge
    • Electron

    Browsers can be specified via the –browser flag when using the run command to launch Cypress. npm scripts can be used as shortcuts in package.json to launch Cypress with a specific browser more conveniently. 

    To run tests on browsers:

    $ npx cypress run --browser chrome --spec “cypress/integration/examples/tests/e2e_test.spec.js”

    Here is an example of a package.json file to show how to define the npm script:

    "scripts": {
      "cy:run:chrome": "cypress run --browser chrome",
      "cy:run:firefox": "cypress run --browser firefox"
    }

    Cypress Reporting

    Reporter options can be specified in the cypress.json configuration file or via CLI options. Cypress supports the following reporting capabilities:

    • Mocha Built-in Reporting – As Cypress is built on top of Mocha, it has the default Mochawesome reporting 
    • JUnit and TeamCity – These 3rd party Mocha reporters are built into Cypress.

    To install additional dependencies for report generation: 

    Installing Mochaawesome:

    $ npm install mochawesome

    Or installing JUnit:

    $ npm install junit

    Examples of a config file and CLI for the Mochawesome report 

    • Cypress.json config file:
    {
        "reporter": "mochawesome",
        "reporterOptions":
        {
            "reportDir": "cypress/results",
            "overwrite": false,
            "html": true,
            "json": true
        }
    }

    • CLI Reporting:
    $ npx cypress run --reporter mochawesome --spec “cypress/integration/examples/tests/e2e_test.spec.js”

    Examples of a config File and CLI for the JUnit Report: 

    • Cypress.json config file for JUnit: 
    {
        "reporter": "junit",
        "reporterOptions": 
        {
            "reportDir": "cypress/results",
            "mochaFile": "results/my-test-output.xml",
            "toConsole": true
        }
    }

    • CLI Reporting: <cypress_junit_reporting></cypress_junit_reporting>
    $ npx cypress run --reporter junit  --reporter-options     “mochaFile=results/my-test-output.xml,toConsole=true

    Fig:- Collapsed View of Mochawesome Report

     

    Fig:- Expanded View of Mochawesome Report

     

    Fig:- Mochawesome Report Settings

    Additional Possibilities of Using Cypress 

    There are several other things we can do using Cypress that we could not cover in this article, although we’ve covered the most important aspects of the tool..

    Here are some other usages of Cypress that we could not explore here:

    • Continuous integration and continuous deployment with Jenkins 
    • Behavior-driven development (BDD) using Cucumber
    • Automating applications with XHR
    • Test retries and retry ability
    • Custom commands
    • Environment variables
    • Plugins
    • Visual testing
    • Slack integration 
    • Model-based testing
    • GraphQL API Testing 

    Limitations with Cypress

    Cypress is a great tool with a great community supporting it. Although it is still young, it is being continuously developed and is quickly catching up with the other full-stack automation tools on the market.

    So, before you decide to use Cypress, we would like to touch upon some of its limitations. These limitations are for version 5.2.0, the latest version of Cypress at the time of this article’s publishing.

    Here are the current limitations of using Cypress:

    • It can’t use two browsers at the same time.
    • It doesn’t provide support for multi-tabs.
    • It only supports the JavaScript language for creating test cases.
    • It doesn’t currently provide support for browsers like Safari and IE.
    • It has limited support for iFrames.

    Conclusion

    Cypress is a great tool with a growing feature-set. It makes setting up, writing, running, and debugging tests easy for QA automation engineers. It also has a quicker learning cycle with a good, baked-in execution environment.

    It is fully JavaScript/MochaJS-oriented with specific new APIs to make scripting easier. It also provides a flexible test execution plan that can implement significant and unexpected changes.

    In this blog, we talked about how Cypress works functionally, performed end-to-end UI testing, and touched upon its limitations. We hope you learned more about using Cypress as a full-stack test automation tool.

    Related QA Articles

    1. Building a scalable API testing framework with Jest and SuperTest
    2. Automation testing with Nightwatch JS and Cucumber: Everything you need to know
    3. API testing using Postman and Newman
  • Kubernetes CSI in Action: Explained with Features and Use Cases

    Kubernetes Volume plugins have been a great way for the third-party storage providers to support a block or file storage system by extending the Kubernetes volume interface and are “In-Tree” in nature.

    In this post, we will dig into Kubernetes Container Storage Interface. We will use Hostpath CSI Driver locally on a single node bare metal cluster, to get the conceptual understanding of the CSI workflow in provisioning the Persistent Volume and its lifecycle. Also, a cool feature of snapshotting the volume and recover it back is explained.

    Introduction

    CSI is a standard for exposing  storage systems in arbitrary block and file storage to containerized workloads on Container Orchestrations like Kubernetes, Mesos, and Cloud Foundry. It becomes very extensible for third-party storage provider to expose their new storage systems using CSI, without actually touching the Kubernetes code. Single independent implementation of CSI Driver by a storage provider will work on any orchestrator.

    This new plugin mechanism has been one of the most powerful features of Kubernetes. It enables the storage vendors to:

    1. Automatically create storage when required.
    2. Make storage available to containers wherever they’re scheduled.
    3. Automatically delete the storage when no longer needed.

    This decoupling helps the vendors to maintain the independent release and feature cycles and focus on the API implementation without actually worrying about the backward incompatibility and to support their plugin just as easy as deploying a few pods.

     

    Image Source: Weekly Geekly

    Why CSI?

    Prior to CSI, k8s volume plugins have to be “In-tree”, compiled and shipped with core kubernetes binaries. This means, it will require the storage providers to check-in their into the core k8s codebase if they wish to add the support for a new storage system.

    A plugin-based solution, flex-volume, tried to address this issue by exposing the exec based API for external  plugins. Although it also tried to work on the similar notion of being detached with k8s binary, there were several major problems with that approach. Firstly, it needed the root access to the host and master file system to deploy the driver files. 

    Secondly, it comes with the huge baggage of prerequisites and OS dependencies which are assumed to be available on the host. CSI implicitly solves all these issues by being containerized and using the k8s storage primitives.

    CSI has evolved as the one-stop solution addressing all the above issues which enables storage plugins to be out-of-tree and deployed via standard k8s primitives, such as PVC, PV and StorageClasses.

    The main aim of introducing CSI is to establish a standard mechanism of exposing any type of storage system under-the-hood for all the container orchestrators.

    Deploy the Driver Plugin

    The CSI Driver comprises of a few main components which are various side cars and also the implementation of the CSI Services by the vendor, which will be understood by the Cos. The CSI Services will be described later in the blog. Let’s try out deploying hostpath CSI Driver.

    Prerequisites:

    • Kubernetes cluster (not Minikube or Microk8s): Running version 1.13 or later
    • Access to the terminal with Kubectl installed

    Deploying HostPath Driver Plugin:

    1. Clone the repo of HostPath Driver Plugin locally or just copy the deploy and example folder from the root path
    2. Checkout the master branch (if not)
    3. The hostpath driver comprises of manifests for following side-cars: (in ./deploy/master/hostpath/)
      – csi-hostpath-attacher.yaml
      – csi-hostpath-provisioner.yaml
      – csi-hostpath-snapshotter.yaml
      – csi-hostpath-plugin.yaml:
      It will deploy 2 containers, one is node-driver-registrar and a hospath-plugin
    4. The driver also includes separate Service for each component and in the deployment file with statefulsets for the containers
    5. It also deploys Cluster-role-bindings and RBAC rules for each component, maintained in a separate repo
    6. Each Component (side-car) is managed in a separate repository
    7. The /deploy/util/ contains a shell script which handles the complete deployment process
    8. After copying the folder or cloning the repo, just run:    
    $ deploy/kubernetes-latest/deploy-hostpath.sh

         9. The output will be similar to:

    applying RBAC rules
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-provisioner/v1.0.1/deploy/kubernetes/rbac.yaml
    serviceaccount/csi-provisioner created
    clusterrole.rbac.authorization.k8s.io/external-provisioner-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-provisioner-role created
    role.rbac.authorization.k8s.io/external-provisioner-cfg created
    rolebinding.rbac.authorization.k8s.io/csi-provisioner-role-cfg created
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-attacher/v1.0.1/deploy/kubernetes/rbac.yaml
    serviceaccount/csi-attacher created
    clusterrole.rbac.authorization.k8s.io/external-attacher-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-attacher-role created
    role.rbac.authorization.k8s.io/external-attacher-cfg created
    rolebinding.rbac.authorization.k8s.io/csi-attacher-role-cfg created
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v1.0.1/deploy/kubernetes/rbac.yaml
    serviceaccount/csi-snapshotter created
    clusterrole.rbac.authorization.k8s.io/external-snapshotter-runner created
    clusterrolebinding.rbac.authorization.k8s.io/csi-snapshotter-role created
    deploying hostpath components
       deploy/kubernetes-1.13/hostpath/csi-hostpath-attacher.yaml
            using           image: quay.io/k8scsi/csi-attacher:v1.0.1
    service/csi-hostpath-attacher created
    statefulset.apps/csi-hostpath-attacher created
       deploy/kubernetes-1.13/hostpath/csi-hostpath-plugin.yaml
            using           image: quay.io/k8scsi/csi-node-driver-registrar:v1.0.2
            using           image: quay.io/k8scsi/hostpathplugin:v1.0.1
            using           image: quay.io/k8scsi/livenessprobe:v1.0.2
    service/csi-hostpathplugin created
    statefulset.apps/csi-hostpathplugin created
       deploy/kubernetes-1.13/hostpath/csi-hostpath-provisioner.yaml
            using           image: quay.io/k8scsi/csi-provisioner:v1.0.1
    service/csi-hostpath-provisioner created
    statefulset.apps/csi-hostpath-provisioner created
       deploy/kubernetes-1.13/hostpath/csi-hostpath-snapshotter.yaml
            using           image: quay.io/k8scsi/csi-snapshotter:v1.0.1
    service/csi-hostpath-snapshotter created
    statefulset.apps/csi-hostpath-snapshotter created
       deploy/kubernetes-1.13/hostpath/csi-hostpath-testing.yaml
            using           image: alpine/socat:1.0.3
    service/hostpath-service created
    statefulset.apps/csi-hostpath-socat created
    11:43:06 waiting for hostpath deployment to complete, attempt #0
    11:43:16 waiting for hostpath deployment to complete, attempt #1
    11:43:26 waiting for hostpath deployment to complete, attempt #2
    deploying snapshotclass
    volumesnapshotclass.snapshot.storage.k8s.io/csi-hostpath-snapclass created

         10. The driver is deployed, we can check:

    $ kubectl get pods
    
    NAME                          READY   STATUS        RESTARTS    AGE
    csi-hostpath-attacher-0       1/1     Running        0          1m06s
    csi-hostpath-provisioner-0    1/1     Running        0          1m06s
    csi-hostpath-snapshotter-0    1/1     Running        0          1m06s
    csi-hostpathplugin-0          2/2     Running        0          1m06s

    CSI API-Resources:

    $ kubectl api-resources | grep -E "^Name|csi|storage|PersistentVolume"
    
    NAME                     APIGROUP                  NAMESPACED     KIND
    persistentvolumesclaims                            true           PersistentVolumeClaim
    persistentvolume                                   false          PersistentVolume
    csidrivers               csi.storage.k8s.io        false          CSIDrivers
    volumesnapshotclasses    snapshot.storage.k8s.io   false          VolumeSnapshotClass
    volumesnapshotcontents   snapshot.storage.k8s.io   false          VolumeSnapshotContent
    Volumesnapshots          snapshot.storage.k8s.io   true           VolumeSnapshot
    csidrivers               storage.k8s.io            false          CSIDriver
    csinodes                 storage.k8s.io            false          CSINode
    storageclasses           storage.k8s.io            false          VolumeAttachment

    There are resources from core apigroups, storage.k8s.io and resources which created by CRDs snapshot.storage.k8s.io and csi.storage.k8s.io.

    CSI SideCars

    K8s CSI containers are sidecars that simplify the development and deployment of the CSI Drivers on a k8s cluster. Different Drivers have some similar logic to trigger the appropriate operations against the “CSI volume driver” container and update the Kubernetes API as appropriate.

    The common controller (common containers) has to be bundled with the provider-specific containers.

    The official sig-k8s contributors maintain the following basic skeleton containers for any CSI Driver:

    Note: In case of Hostpath driver, only ‘csi-hostpath-plugin’ container will be having the specific code. All the others are common CSI sidecar containers. These containers have a socket mounted in the socket-dir volume of type EmptyDir, which makes their communication possible using gRPC

    1. External Provisioner:
      It  is a sidecar container that watches Kubernetes PersistentVolumeClaim objects and triggers CSI CreateVolume and DeleteVolume operations against a driver endpoint.
      The CSI external-attacher also supports the Snapshot DataSource. If a Snapshot CRD is specified as a data source on a PVC object, the sidecar container fetches the information about the snapshot by fetching the SnapshotContent object and populates the data source field indicating to the storage system that new volume should be populated using specified snapshot.
    2. External Attacher :
      It  is a sidecar container that watches Kubernetes VolumeAttachment objects and triggers CSI ControllerPublish and ControllerUnpublish operations against a driver endpoint
    3. Node-Driver Registrar:
      It is a sidecar container that registers the CSI driver with kubelet, and adds the drivers custom NodeId to a label on the Kubernetes Node API Object. The communication of this sidecar is handled by the ‘Identity-Service’ implemented by the driver. The CSI Driver is registered with the kubelet using its device–plugin mechanisms
    4. External Snapshotter:
      It is a sidecar container that watches the Kubernetes API server for VolumeSnapshot and VolumeSnapshotContent CRD objects.The creation of a new VolumeSnapshot object referencing a SnapshotClass CRD object corresponding to this driver causes the sidecar container to provision a new snapshot.
    5. This sidecar listens to the service which indicates the successful creation of VolumeSnapshot, and immediately creates the VolumeSnapshotContent resource
    6. Cluster-driver Registrar:
      CSI driver is registered with the cluster by a sidecar container CSI cluster-driver-registrar creating a CSIDriver object. This CSIDriver enables the driver to customize the way of k8s interaction with it.

    Developing a CSI Driver

    To start the implementation of CSIDriver, an application must implement the gRPC services described by the CSI Specification.

    The minimum service a CSI application should implement are following:

    • CSI Identity service: Enables Kubernetes components and CSI containers to identify the driver
    • CSI Node service: Required methods enable callers to make volume available at a specified path.

    All the required services may be implemented independently or in the same driver application. The CSI driver application should be containerised to make it easy to deploy on Kubernetes. Once the main specific logic of the driver is containerized, they can be attached to the sidecars and deployed, in node and/or controller mode.

    Capabilities

    CSI also have provisions to enable the custom CSI driver to support many additional features/services by using the “Capabilities”. It contains a list of all the features the driver supports.

    Note: Refer the link for detailed explanation for developing a CSI Driver.

    Try out provisioning the PV:

    1. A storage class with:

    volumeBindingMode: WaitForFirstConsumer
    kind: StorageClass
    apiVersion: storage.k8s.io/v1
    metadata:
      name: csi-hostpath-sc
    provisioner: hostpath.csi.k8s.io
    volumeBindingMode: WaitForFirstConsumer

    2. Now, A PVC is also needed to be consumed by the sample Pod.

    And also a sample pod is also required, so that it can be bounded with the PV created by the PVC from above step
    The above files are found in ./exmples directory and can be deployed using create or apply kubectl commands

    Validate the deployed components:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc-fs
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
      storageClassName: csi-hostpath-sc # defined in csi-setup.yaml

    3. The Pod to consume the PV

    kind: Pod
    apiVersion: v1
    metadata:
      name: pod-fs
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - csi-hostpathplugin
            topologyKey: kubernetes.io/hostname
      containers:
        - name: my-frontend
          image: busybox
          volumeMounts:
          - mountPath: "/data"
            name: my-csi-volume
          command: [ "sleep", "1000000" ]
      volumes:
        - name: my-csi-volume
          persistentVolumeClaim:
            claimName: pvc-fs # defined in csi-pvc.yaml

    Validate the deployed components:

    $ kubectl get pv
    
    NAME                                    CAPACITY ACCESSMODES STATUS CLAIM         STORAGECLASS     
    pvc-58d5ec38-03e5-11e9-be51-000c29e88ff1  1Gi       RWO      Bound  default/pvc-fs csi-hostpath-sc
    $ kubectl get pvc
    
    NAME      STATUS   VOLUME                                     CAPACITY  ACCESS MODES    STORAGECLASS
    csi-pvc   Bound    pvc-58d5ec38-03e5-11e9-be51-000c29e88ff1     1Gi         RWO         csi-hostpath-sc

    Brief on how it works:

    • csi-provisioner issues CreateVolumeRequest call to the CSI socket, then hostpath-plugin calls CreateVolume and informs CSI about its creation
    • csi-provisioner creates PV and updates PVC to be bound and the VolumeAttachment object is created by controller-manager
    • csi-attacher which watches for VolumeAttachments submits ControllerPublishVolume rpc call to hostpath-plugin, then hostpaths-plugin gets ControllerPublishVolume and calls hostpath AttachVolume csi-attacher update VolumeAttachment status
    • All this time kubelet waits for volume to be attached and submits NodeStageVolume (format and mount to the node to the staging dir) to the csi-node.hostpath-plugin
    • csi-node.hostpath-plugin gets NodeStageVolume call and mounts to `/var/lib/kubelet/plugins/kubernetes.io/csi/pv/<pv-name>/globalmount`, then responses to kubelet</pv-name>
    • kubelet calls NodePublishVolume (mount volume to the pod’s dir)
    • csi-node.hostpath-plugin performs NodePublishVolume and mounts the volume to `/var/lib/kubelet/pods/<pod-uuid>/volumes/</pod-uuid>kubernetes.io~csi/<pvc-name>/mount`</pvc-name>

      Finally, kubelet starts container of the pod with the provisioned volume.


    Let’s confirm the working of Hostpath CSI driver:

    The Hostpath driver is configured to create new volumes in the hostpath container in the plugin daemonset under the ‘/tmp’ directory. This path persist as long as the DaemonSet pod is up and running.

    If a file is written in the hostpath mounted volume in an application pod, should be seen in the hostpath cotainer.A file written in a properly mounted Hostpath volume inside an application should show up inside the Hostpath container.

    1. To try out the above statement, Create a file on application pod

    $ kubectl exec -it pod-fs /bin/sh
    
    / # touch /data/my-test
    / # exit

    2. And then exec in the hostpath container and run ‘ls’ command to check

    $ kubectl exec -it $(kubectl get pods --selector app=csi-hostpathplugin 
    -o jsonpath='{.items[*].metadata.name}') -c hostpath /bin/sh
    
    / # find /tmp -name my-test
    /tmp/057485ab-c714-11e8-bb16-000c2967769a/my-test
    / # exit

    Note: The better way of the verification is to inspect the VolumeAttachment object created that represents the attached volume API object created that represents the attached volume

    Support for Snapshot

    Volume Snapshotting is introduced as an Alpha feature for the Kubernetes persistent volume in v1.12. 

    Being an alpha feature, ‘VolumeSnapshotDataSource’ feature gate needs to be enabled. This feature opens a pool of use cases of keeping the snapshot of data locally. The API objects used are VolumeSnapshot, VolumeSnapshotContent and VolumeSnapshotClass. It was developed with a similar notion and relationship of PV, PVC and StorageClass. 

    To create a snapshot, the VolumeSnapshot object needs to be created with the source as PVC and VolumeSnapshotClass

    and the CSI-Snapshotter container will create a VolumeSnaphsotContent.

    Let’s try out with an example:

    Just like the provisioner create a PV for us when a PVC is created, similarly a VolumeSnapshotContent object will be created when VolumeSnapshot object is created.

    apiVersion: snapshot.storage.k8s.io/v1alpha1
    kind: VolumeSnapshot
    metadata:
      name: fs-pv-snapshot
    spec:
      snapshotClassName: csi-hostpath-snapclass
      source:
        name: pvc-fs
        kind: PersistentVolumeClaim

    The volumesnapshotcontent is created. The output will look like:

    $ kubectl get volumesnapshotcontent
     
    NAME                                                  AGE
    snapcontent-f55db632-c716-11e8-8911-000c2967769a      14s

    Restore from the snapshot:

    The DataSource field in the PVC can accept the source of kind: VolumeSnapsot which will create a new PV from that volume snapshot, when a Pod is bound to this PVC.

    The new PV will be having the same data as of the PV from which the snapshot was taken and it can be attached to any other pod. The new pod having that PV, proves of the possible “Restore” and “Cloning” use cases.

    Tear Down CSI-Hostpath installation:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: fs-pvc-restore
    spec:
      storageClassName: csi-hostpath-sc
      dataSource:
        name: fs-pv-snapshot
        kind: VolumeSnapshot
        apiGroup: snapshot.storage.k8s.io
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi

    And we’re done here.

    Conclusion

    Since Kubernetes has started supporting the Raw Block Device type of Persistent Volume, hospath driver and any other driver may also support it, which will be explained in the next part of the blog. In this blog, we got deep understanding of the CSI, its components and services. The new features of CSI and the problems it solves. The CSI Hostpath driver was deeply in this blog to experiment and understand the provisioner, snapshotter flows for PV and VolumeSnapshots. Also, the PV snapshot, restore and cloning use cases were demonstrated.

  • Game of Hackathon 2019: A Glimpse of Our First Hackathon

    Hackathons for technology startups are like picking up a good book. It may take a long time before you start, but once you do, you wonder why you didn’t do it sooner? Last Friday, on 31st May 2019, we conducted our first Hackathon at Velotio and it was a grand success!

    Although challenging projects from our clients are always pushing us to learn new things, we saw a whole new level of excitement and enthusiasm among our employees to bring their own ideas to life during the event. The 12-hour Hackathon saw participation from 15 teams, a lot of whom came well-prepared in advance with frameworks and ideas to start building immediately.

    Here are some pictures from the event:

    The intense coding session was then followed by a series of presentations where all the teams showcased their solutions.

    The first prize was bagged by Team Mitron who worked on a performance review app and was awarded a cash prize of 75,000 Rs.

    The second prize of 50,000 Rs. was awarded to Team WireQ. Their solution was an easy sync up platform that would serve as a single source of truth for the designers, developers, and testers to work seamlessly on projects together — a problem we have often struggled with in-house as well.

    Our QA Team put together a complete test suite framework that would perform all functional and non-functional testing activities, including maintaining consistency in testing, minimal code usage, improvement in test structuring and so on. They won the third prize worth 25,000 Rs.

    Our heartiest congratulations to all the winners!

    This Hackathon has definitely injected a lot of positive energy and innovation into our work culture and got so many of us to collaborate more effectively and learn from each other. We cannot wait to do our next Hackathon and share more with you all.

    Until then, stay tuned!

  • Introduction to the Modern Server-side Stack – Golang, Protobuf, and gRPC

    There are some new players in town for server programming and this time it’s all about Google. Golang has rapidly been gaining popularity ever since Google started using it for their own production systems. And since the inception of Microservice Architecture, people have been focusing on modern data communication solutions like gRPC along with Protobuf. In this post, I will walk you through each of these briefly.

    Golang

    Golang or Go is an open source, general purpose programming language by Google. It has been gaining popularity recently for all the good reasons. It may come as a surprise to most people that language is almost 10 years old and has been production ready for almost 7 years, according to Google.

    Golang is designed to be simple, modern, easy to understand, and quick to grasp. The creators of the language designed it in such a way that an average programmer can have a working knowledge of the language over a weekend. I can attest to the fact that they definitely succeeded. Speaking of the creators, these are the experts that have been involved in the original draft of the C language so we can be assured that these guys know what they are doing.

    That’s all good but why do we need another language?

    For most of the use cases, we actually don’t. In fact, Go doesn’t solve any new problems that haven’t been solved by some other language/tool before. But it does try to solve a specific set of relevant problems that people generally face in an efficient, elegant, and intuitive manner. Go’s primary focus is the following:

    • First class support for concurrency
    • An elegant, modern language that is very simple to its core
    • Very good performance
    • First hand support for the tools required for modern software development

    I’m going to briefly explain how Go provides all of the above. You can read more about the language and its features in detail from Go’s official website.

    Concurrency

    Concurrency is one of the primary concerns in most of the server applications and it should be the primary concern of the language, considering the modern microprocessors. Go introduces a concept called a ‘goroutine’. A ‘goroutine’ is analogous to a ‘lightweight user-space thread’. It is much more complicated than that in reality as several goroutines multiplex on a single thread but the above expression should give you a general idea. These are light enough that you can actually spin up a million goroutines simultaneously as they start with a very tiny stack. In fact, that’s recommended. Any function/method in Go can be used to spawn a Goroutine. You can just do ‘go myAsyncTask()’ to spawn a goroutine from ‘myAsyncTask’ function. The following is an example:

    // This function performs the given task concurrently by spawing a goroutine
    // for each of those tasks.
    
    func performAsyncTasks(task []Task) {
      for _, task := range tasks {
        // This will spawn a separate goroutine to carry out this task.
        // This call is non-blocking
        go task.Execute()
      }
    }

    Yes, it’s that easy and it is meant to be that way as Go is a simple language and you are expected to spawn a goroutine for every independent async task without caring much. Go’s runtime automatically takes care of running the goroutines in parallel if multiple cores are available. But how do these goroutines communicate? The answer is channels.

    ‘Channel’ is also a language primitive that is meant to be used for communication among goroutines. You can pass anything from a channel to another goroutine (A primitive Go type or a Go struct or even other channels). A channel is essentially a blocking double ended queue (can be single ended too). If you want a goroutine(s) to wait for a certain condition to be met before continuing further you can implement cooperative blocking of goroutines with the help of channels.

    These two primitives give a lot of flexibility and simplicity in writing asynchronous or parallel code. Other helper libraries like a goroutine pool can be easily created from the above primitives. One basic example is:

    package executor
    
    import (
    	"log"
    	"sync/atomic"
    )
    
    // The Executor struct is the main executor for tasks.
    // 'maxWorkers' represents the maximum number of simultaneous goroutines.
    // 'ActiveWorkers' tells the number of active goroutines spawned by the Executor at given time.
    // 'Tasks' is the channel on which the Executor receives the tasks.
    // 'Reports' is channel on which the Executor publishes the every tasks reports.
    // 'signals' is channel that can be used to control the executor. Right now, only the termination
    // signal is supported which is essentially is sending '1' on this channel by the client.
    type Executor struct {
    	maxWorkers    int64
    	ActiveWorkers int64
    
    	Tasks   chan Task
    	Reports chan Report
    	signals chan int
    }
    
    // NewExecutor creates a new Executor.
    // 'maxWorkers' tells the maximum number of simultaneous goroutines.
    // 'signals' channel can be used to control the Executor.
    func NewExecutor(maxWorkers int, signals chan int) *Executor {
    	chanSize := 1000
    
    	if maxWorkers > chanSize {
    		chanSize = maxWorkers
    	}
    
    	executor := Executor{
    		maxWorkers: int64(maxWorkers),
    		Tasks:      make(chan Task, chanSize),
    		Reports:    make(chan Report, chanSize),
    		signals:    signals,
    	}
    
    	go executor.launch()
    
    	return &executor
    }
    
    // launch starts the main loop for polling on the all the relevant channels and handling differents
    // messages.
    func (executor *Executor) launch() int {
    	reports := make(chan Report, executor.maxWorkers)
    
    	for {
    		select {
    		case signal := <-executor.signals:
    			if executor.handleSignals(signal) == 0 {
    				return 0
    			}
    
    		case r := <-reports:
    			executor.addReport(r)
    
    		default:
    			if executor.ActiveWorkers < executor.maxWorkers && len(executor.Tasks) > 0 {
    				task := <-executor.Tasks
    				atomic.AddInt64(&executor.ActiveWorkers, 1)
    				go executor.launchWorker(task, reports)
    			}
    		}
    	}
    }
    
    // handleSignals is called whenever anything is received on the 'signals' channel.
    // It performs the relevant task according to the received signal(request) and then responds either
    // with 0 or 1 indicating whether the request was respected(0) or rejected(1).
    func (executor *Executor) handleSignals(signal int) int {
    	if signal == 1 {
    		log.Println("Received termination request...")
    
    		if executor.Inactive() {
    			log.Println("No active workers, exiting...")
    			executor.signals <- 0
    			return 0
    		}
    
    		executor.signals <- 1
    		log.Println("Some tasks are still active...")
    	}
    
    	return 1
    }
    
    // launchWorker is called whenever a new Task is received and Executor can spawn more workers to spawn
    // a new Worker.
    // Each worker is launched on a new goroutine. It performs the given task and publishes the report on
    // the Executor's internal reports channel.
    func (executor *Executor) launchWorker(task Task, reports chan<- Report) {
    	report := task.Execute()
    
    	if len(reports) < cap(reports) {
    		reports <- report
    	} else {
    		log.Println("Executor's report channel is full...")
    	}
    
    	atomic.AddInt64(&executor.ActiveWorkers, -1)
    }
    
    // AddTask is used to submit a new task to the Executor is a non-blocking way. The Client can submit
    // a new task using the Executor's tasks channel directly but that will block if the tasks channel is
    // full.
    // It should be considered that this method doesn't add the given task if the tasks channel is full
    // and it is up to client to try again later.
    func (executor *Executor) AddTask(task Task) bool {
    	if len(executor.Tasks) == cap(executor.Tasks) {
    		return false
    	}
    
    	executor.Tasks <- task
    	return true
    }
    
    // addReport is used by the Executor to publish the reports in a non-blocking way. It client is not
    // reading the reports channel or is slower that the Executor publishing the reports, the Executor's
    // reports channel is going to get full. In that case this method will not block and that report will
    // not be added.
    func (executor *Executor) addReport(report Report) bool {
    	if len(executor.Reports) == cap(executor.Reports) {
    		return false
    	}
    
    	executor.Reports <- report
    	return true
    }
    
    // Inactive checks if the Executor is idle. This happens when there are no pending tasks, active
    // workers and reports to publish.
    func (executor *Executor) Inactive() bool {
    	return executor.ActiveWorkers == 0 && len(executor.Tasks) == 0 && len(executor.Reports) == 0
    }

    Simple Language

    Unlike a lot of other modern languages, Golang doesn’t have a lot of features. In fact, a compelling case can be made for the language being too restrictive in its feature set and that’s intended. It is not designed around a programming paradigm like Java or designed to support multiple programming paradigms like Python. It’s just bare bones structural programming. Just the essential features thrown into the language and not a single thing more.

    After looking at the language, you may feel that the language doesn’t follow any particular philosophy or direction and it feels like every feature is included in here to solve a specific problem and nothing more than that. For example, it has methods and interfaces but not classes; the compiler produces a statically linked binary but still has a garbage collector; it has strict static typing but doesn’t support generics. The language does have a thin runtime but doesn’t support exceptions.

    The main idea here that the developer should spend the least amount of time expressing his/her idea or algorithm as code without thinking about “What’s the best way to do this in x language?” and it should be easy to understand for others. It’s still not perfect, it does feel limiting from time to time and some of the essential features like Generics and Exceptions are being considered for the ‘Go 2’.

    Performance

    Single threaded execution performance NOT a good metric to judge a language, especially when the language is focused around concurrency and parallelism. But still, Golang sports impressive benchmark numbers only beaten by hardcore system programming languages like C, C++, Rust, etc. and it is still improving. The performance is actually very impressive considering its a Garbage collected language and is good enough for almost every use case.

    (Image Source: Medium)

    Developer Tooling

    The adoption of a new tool/language directly depends on its developer experience. And the adoption of Go does speak for its tooling. Here we can see that same ideas and tooling is very minimal but sufficient. It’s all achieved by the ‘go’ command and its subcommands. It’s all command line.

    There is no package manager for the language like pip, npm. But you can get any community package by just doing

    go get github.com/velotiotech/WebCrawler/blob/master/executor/executor.go

    CODE: https://gist.github.com/velotiotech/3977b7932b96564ac9a041029d760d6d.js

    Yes, it works. You can just pull packages directly from github or anywhere else. They are just source files.

    But what about package.json..? I don’t see any equivalent for `go get`. Because there isn’t. You don’t need to specify all your dependency in a single file. You can directly use:

    import "github.com/xlab/pocketsphinx-go/sphinx"

    In your source file itself and when you do `go build` it will automatically `go get` it for you. You can see the full source file here:

    package main
    
    import (
    	"encoding/binary"
    	"bytes"
    	"log"
    	"os/exec"
    
    	"github.com/xlab/pocketsphinx-go/sphinx"
    	pulse "github.com/mesilliac/pulse-simple" // pulse-simple
    )
    
    var buffSize int
    
    func readInt16(buf []byte) (val int16) {
    	binary.Read(bytes.NewBuffer(buf), binary.LittleEndian, &val)
    	return
    }
    
    func createStream() *pulse.Stream {
    	ss := pulse.SampleSpec{pulse.SAMPLE_S16LE, 16000, 1}
    	buffSize = int(ss.UsecToBytes(1 * 1000000))
    	stream, err := pulse.Capture("pulse-simple test", "capture test", &ss)
    	if err != nil {
    		log.Panicln(err)
    	}
    	return stream
    }
    
    func listen(decoder *sphinx.Decoder) {
    	stream := createStream()
    	defer stream.Free()
    	defer decoder.Destroy()
    	buf := make([]byte, buffSize)
    	var bits []int16
    
    	log.Println("Listening...")
    
    	for {
    		_, err := stream.Read(buf)
    		if err != nil {
    			log.Panicln(err)
    		}
    
    		for i := 0; i < buffSize; i += 2 {
    			bits = append(bits, readInt16(buf[i:i+2]))
    		}
    
    		process(decoder, bits)
    		bits = nil
    	}
    }
    
    func process(dec *sphinx.Decoder, bits []int16) {
    	if !dec.StartUtt() {
    		panic("Decoder failed to start Utt")
    	}
    	
    	dec.ProcessRaw(bits, false, false)
    	dec.EndUtt()
    	hyp, score := dec.Hypothesis()
    	
    	if score > -2500 {
    		log.Println("Predicted:", hyp, score)
    		handleAction(hyp)
    	}
    }
    
    func executeCommand(commands ...string) {
    	cmd := exec.Command(commands[0], commands[1:]...)
    	cmd.Run()
    }
    
    func handleAction(hyp string) {
    	switch hyp {
    		case "SLEEP":
    		executeCommand("loginctl", "lock-session")
    		
    		case "WAKE UP":
    		executeCommand("loginctl", "unlock-session")
    
    		case "POWEROFF":
    		executeCommand("poweroff")
    	}
    }
    
    func main() {
    	cfg := sphinx.NewConfig(
    		sphinx.HMMDirOption("/usr/local/share/pocketsphinx/model/en-us/en-us"),
    		sphinx.DictFileOption("6129.dic"),
    		sphinx.LMFileOption("6129.lm"),
    		sphinx.LogFileOption("commander.log"),
    	)
    	
    	dec, err := sphinx.NewDecoder(cfg)
    	if err != nil {
    		panic(err)
    	}
    
    	listen(dec)
    }

    This binds the dependency declaration with source itself.

    As you can see by now, it’s simple, minimal and yet sufficient and elegant. There is first hand support for both unit tests and benchmarks with flame charts too. Just like the feature set, it also has its downsides. For example, `go get` doesn’t support versions and you are locked to the import URL passed in you source file. It is evolving and other tools have come up for dependency management.

    Golang was originally designed to solve the problems that Google had with their massive code bases and the imperative need to code efficient concurrent apps. It makes coding applications/libraries that utilize the multicore nature of modern microchips very easy. And, it never gets into a developer’s way. It’s a simple modern language and it never tries to become anything more that that.

    Protobuf (Protocol Buffers)

    Protobuf or Protocol Buffers is a binary communication format by Google. It is used to serialize structured data. A communication format? Kind of like JSON? Yes. It’s more than 10 years old and Google has been using it for a while now.

    But don’t we have JSON and it’s so ubiquitous…

    Just like Golang, Protobufs doesn’t really solve anything new. It just solves existing problems more efficiently and in a modern way. Unlike Golang, they are not necessarily more elegant than the existing solutions. Here are the focus points of protobuf:

    • It’s a binary format, unlike JSON and XML, which are text based and hence it’s vastly space efficient.
    • First hand and sophisticated support for schemas.
    • First hand support for generating parsing and consumer code in various languages.

    Binary format and speed

    So are protobuf really that fast? The short answer is, yes. According to the Google Developers they are 3 to 10 times smaller and 20 to 100 times faster than XML. It’s not a surprise as it is a binary format, the serialized data is not human readable.

    (Image Source: Beating JSON performance with Protobuf)

    Protobufs take a more planned approach. You define `.proto` files which are kind of the schema files but are much more powerful. You essentially define how you want your messages to be structured, which fields are optional or required, their data types etc. After that the protobuf compiler will generate the data access classes for you. You can use these classes in your business logic to facilitate communication.

    Looking at a `.proto` file related to a service will also give you a very clear idea of the specifics of the communication and the features that are exposed. A typical .proto file looks like this:

    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;
    
      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }
    
      repeated PhoneNumber phone = 4;
    }

    Fun Fact: Jon Skeet, the king of Stack Overflow is one of the main contributors in the project.

    gRPC

    gRPC, as you guessed it, is a modern RPC (Remote Procedure Call) framework. It is a batteries included framework with built in support for load balancing, tracing, health checking, and authentication. It was open sourced by Google in 2015 and it’s been gaining popularity ever since.

    An RPC framework…? What about REST…?

    SOAP with WSDL has been used long time for communication between different systems in a Service Oriented Architecture. At the time, the contracts used to be strictly defined and systems were big and monolithic, exposing a large number of such interfaces.

    Then came the concept of ‘browsing’ where the server and client don’t need to be tightly coupled. A client should be able to browse service offerings even if they were coded independently. If the client demanded the information about a book, the service along with what’s requested may also offer a list of related books so that client can browse. REST paradigm was essential to this as it allows the server and client to communicate freely without strict restriction using some primitive verbs.

    As you can see above, the service is behaving like a monolithic system, which along with what is required is also doing n number of other things to provide the client with the intended `browsing` experience. But this is not always the use case. Is it?

    Enter the Microservices

    There are many reasons to adopt for a Microservice Architecture. The prominent one being the fact that it is very hard to scale a Monolithic system. While designing a big system with Microservices Architecture each business or technical requirement is intended to be carried out as a cooperative composition of several primitive ‘micro’ services.

    These services don’t need to be comprehensive in their responses. They should perform specific duties with expected responses. Ideally, they should behave like pure functions for seamless composability.

    Now using REST as a communication paradigm for such services doesn’t provide us with much of a benefit. However, exposing a REST API for a service does enable a lot of expression capability for that service but again if such expression power is neither required nor intended we can use a paradigm that focuses more on other factors.

    gRPC intends to improve upon the following technical aspects over traditional HTTP requests:

    • HTTP/2 by default with all its goodies.
    • Protobuf as machines are talking.
    • Dedicated support for streaming calls thanks to HTTP/2.
    • Pluggable auth, tracing, load balancing and health checking because you always need these.

    As it’s an RPC framework, we again have concepts like Service Definition and Interface Description Language which may feel alien to the people who were not there before REST but this time it feels a lot less clumsy as gRPC uses Protobuf for both of these.

    Protobuf is designed in such a way that it can be used as a communication format as well as a protocol specification tool without introducing anything new. A typical gRPC service definition looks like this:

    service HelloService {
      rpc SayHello (HelloRequest) returns (HelloResponse);
    }
    
    message HelloRequest {
      string greeting = 1;
    }
    
    message HelloResponse {
      string reply = 1;
    }

    You just write a `.proto` file for your service describing the interface name, what it expects, and what it returns as Protobuf messages. Protobuf compiler will then generate both the client and server side code. Clients can call this directly and server-side can implement these APIs to fill in the business logic.

    Conclusion

    Golang, along with gRPC using Protobuf is an emerging stack for modern server programming. Golang simplifies making concurrent/parallel applications and gRPC with Protobuf enables efficient communication with a pleasing developer experience.

  • Building Type Safe Backend Apps with Typegoose and TypeGraphQL

    In this article, we will be trying to solve the most common problem encountered while trying to model MongoDB backend schema with TypeScript and Mongoose. We will also try to address and solve the difficulties of maintaining GraphQL types. 

    Almost every serious JavaScript developer uses TypeScript. However, many aged libraries do not support it natively, which becomes an increasing issue as the project grows. Then, if you add up GraphQL, which is a great modern API development solution, it becomes too much of a boilerplate.

    Prerequisites

    This article assumes that you have working knowledge of TypeScript, MongoDB, and GraphQL. We’ll be using Mongoose for specifying models, which is the go-to Object Document Mapper (ODM) solution for MongoDB.

    Let’s consider a basic example of a Mongoose model written in TypeScript. This might look something like the one mentioned below, a user model with basic model properties (email, first name, last name, and password):

    import { Document, Model, Schema } from "mongoose";
    import { db } from "../util/database";
    
    export interface IUserProps {
      email: string;
      firstName: string;
      lastName: string;
      password: string;
    }
    
    export interface IUserDocument extends IUserProps, Document {
    }
    
    export interface IUserModel extends Model<IUserDocument> {
      dateCreated: Date;
      lastUpdated: Date;
      hashPassword(password: string): string;
    }
    
    const UserSchema: Schema = new Schema(
      {
        email: {
          type: String,
          unique: true,
        },
        firstName: {
          type: String,
        },
        password: {
          type: String,
        },
      },
      { timestamps: true }
    );
    
    const hashPassword = (_password: string) => {
      // logic to hash passwords
    }
    
    UserSchema.method("hashPassword", hashPassword);
    
    export const User: IUserModel = db.model<IUserDocument, IUserModel>(
      "User",
      UserSchema
    );

    As you can see, it would be cumbersome to add and maintain interfaces manually with Mongoose. We would need at least 2-3 interfaces to occupy the typing needs to get model properties and methods working with proper typing.

    Moving forward to add our queries and mutations, we need to create resolvers for the model above, assuming we have a service that deals with models. Here’s what our resolver looks like:

    import { ObjectId } from 'bson';
    import { IResolvers } from 'graphql-tools';
    import { IUserProps } from './user.model';
    import { UserService } from './user.service';
    
    const userService = new UserService();
    export const userResolvers: IResolvers = {
      Query: {
        User: (_root: unknown, args: { id: ObjectId }) => userService.get(args.id),
        //...
      },
      Mutation: {
        createUser: async (_root: unknown, args: IUserProps) => await userService.create(args),
        //...
      }
    };

    Not bad, we got our model and service and the resolver also looks good. But wait, we need to add GraphQL types as well. Here we are intentionally not including inputs to keep it short. Let’s do that:

    type Query {
      User(id: String): User
    }
    
    type Mutation {
      createUser(
        email: String,
        firstName: String,
        lastName: String,
        password: String,
      ): User
    }
    
    type User {
      id: String!
      email: String!
      firstName: String!
      lastName: String!
      password: String!
    }

    Now, we have to club the schemas and resolvers together then pass them onto the GraphQL Express server—Apollo Server in this case:

    import * as path from 'path';
    import * as fs from 'fs';
    import { ApolloServer } from 'apollo-server'
    import { makeExecutableSchema }  from 'graphql-tools';
    import { resolvers } from './src/resolvers';
    
    const userSchema = path.join(__dirname, 'src/user/user.schema.graphql');
    const schemaDef = fs.readFileSync(userSchema, 'utf8');
    
    const schema = makeExecutableSchema({ typeDefs: schemaDef });
    
    const server = new ApolloServer({ schema, resolvers });
    
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
    });

    With this setup, we got four files per model: model, resolver, service, and GraphQL schema file.

    That’s too many things to keep in sync in real life. Imagine you need to add a new property to the above model after reaching production. You’ll end up doing at least following:

    1. Add a migration to sync the DB
    2. Update the interfaces
    3. Update the model schema
    4. Update the GraphQL schema

    Possible Solution

    As we know, after this setup, we’re mostly dealing with the entity models and struggling to keep its types and relations in sync.

    If the model itself can handle it somehow, we can definitely save some effort, which  means we can sort things out if these entity model classes can represent both the database schema and its types.

    Adding TypeGoose

    Mongoose schema declarations with TypeScript can get tricky—or there might be a better way. Let’s add TypeGoose, so you no longer have to maintain interfaces (arguably). Here’s what the same user model looks like:

    import { DocumentType, getModelForClass, prop as Property } from '@typegoose/typegoose';
    import { getSchemaOptions } from 'src/util/typegoose';
    import { Field as GqlField, ObjectType as GqlType } from 'type-graphql';
    
    export class User {
      readonly _id: string;
    
      @Property({ required: true })
      firstName: string;
    
      @Property({ required: false })
      lastName: string;
    
      @Property({ required: true })
      password: string;
    
      @Property({ required: true, unique: true })
      email: string;
    
      hashPassword(this: DocumentType<User>, _password: string) {
        // logic to hash passwords
      }
    }

    Alright, no need for adding interfaces for the model and documents. You could have an interface for model implementation, but it’s not necessary.

    With Reflect, which is used internally by TypeGoose, we managed to skip the need for additional interfaces.

    If we want to add custom validations and messages, TypeGoose allows us to do that too. The prop decorator offers almost all the things you can expect from a mongoose model schema definition.

    @Property({ required: false, unique: true })

    Adding TypeGraphQL

    Alright, TypeGoose has helped us with handling mongoose schema smoothly. But, we still need to define types for GraphQL. Also, we need to update the model types whenever we change our models. 

    Let’s add TypeGraphQL

    import { DocumentType, getModelForClass, prop as Property } from '@typegoose/typegoose';
    import { getSchemaOptions } from 'src/util/typegoose';
    import { Field as GqlField, ObjectType as GqlType } from 'type-graphql';
    
    @GqlType()
    export class User {
      @GqlField(_type => String)
      readonly _id: string;
    
      @GqlField(_type => String)
      @Property({ required: true })
      firstName: string;
    
      @GqlField(_type => String, { nullable: true })
      @Property({ required: false })
      lastName: string;
    
      @GqlField(_type => String)
      @Property({ required: true })
      password: string;
    
      @GqlField(_type => String)
      @Property({ required: true, unique: true })
      email: string;
    
      hashPassword(this: DocumentType<User>, _password: string) {
        // logic to hash passwords
      }
    }

    What we just did is use the same TypeScript user class to define the schema as well as its GraphQL type—pretty neat.

    Because we have added TypeGraphQL, our resolvers no longer need extra interfaces. We can add input classes for parameter types. Consider common input types such as CreateInput, UpdateInput, and FilterInput.

    import { Arg, Ctx, Mutation, Resolver } from 'type-graphql';
    import { User } from './user.model';
    import { UserService } from './user.service';
    
    @Resolver(_of => User)
    
    export class UserResolver {
      private __userService: UserService;
    
      constructor() {
        this.__userService = new UserService();    
      }
    
      @Mutation(_returns => User)
      async createUser(@Arg('data', type => UserCreateInput) data: UserCreateInput, @Ctx() ctx: any) {
       return this.__userService.create(data)
      }
    }

    You can learn more about the syntax and input definition in the official docs.

    That’s it. We are ready with our setup, and we can now simply build a schema and pass it to the server entry point just like that. No need to import schema files and merge resolvers. Simply pass array of resolvers to buildSchema:

    import {ApolloServer} from 'apollo-server'
    
    import { resolvers } from './src/resolvers';
    import { buildSchema }  from 'type-graphql';
    
    const schema = buildSchema({
      resolvers,
    });
    
    const server = new ApolloServer({ schema, resolvers });
    
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
    });

    Once implemented, this is how our custom demo project architecture might look:

    Fig:- Application Architecture

    Limitations and Alternatives

    Though these packages save some work for us, one may decide not to go for them since they use experimental features such as experimental decorators. However, the acceptance of these experimental features is growing.

    TypeGoose:

    Though TypeGoose offers a great extension to Mongoose, they’ve recently introduced some breaking changes. Upgrading from recent versions might be a risk. One alternative to TypeGoose for decorator-based schema definitions is TypeORM. Though, it currently has basic experimental support for MongoDB. 

    TypeGraphQL:

    TypeGraphQL is a well-maintained library. There are other options, like Nest.js and graphql-schema-decorators, which supports decorators for GraphQL schema. 

    However, as Nest.js’s GraphQL support is more framework-oriented, it might be more than needed. The other one is not supported any longer. You can even integrate TypeGraphQL with Nest.js with some caveats.

    Conclusion

    Unsurprisingly, both of these libraries use experimental decorators API with Reflect Metadata. Reflect Metadata adds additional metadata support to the class and its members. The concept might look innovative but it’s nothing new. Languages like C# and Java support attributes or annotations that add metadata to types. With these added, it becomes handy to create and maintain well-typed applications.

    One thing to note here would be—though the article introduces the benefits of using TypeGraphQL and TypeGoose together—it does not mean you can’t use them separately. Depending upon your requirements, you may use either of the tools or a combination of them.

    This article covers a very basic setup for introduction of the mentioned technologies. You might want to learn more about advanced real-life needs with these tools and techniques from some of the articles mentioned below.

    Further Reading

    You can find the referenced code at this repo.

  • Building an Intelligent Chatbot Using Botkit and Rasa NLU

    Introduction

    Bots are the flavor of the season. Everyday, we hear about a new bot catering to domains like travel, social, legal, support, sales, etc. being launched. Facebook Messenger alone has more than 11,000 bots when I last checked and must have probably added thousands of them as I write this article.

    The first generation of bots were dumb since they could understand only a limited set of queries based on keywords in the conversation. But the commoditization of NLP(Natural Language Processing) and machine learning by services like Wit.ai, API.ai, Luis.ai, Amazon Lex, IBM Watson, etc. has resulted in the growth of intelligent bots like donotpay, chatShopper. I don’t know if bots are just hype or the real deal. But I can say with certainty that building a bot is fun and challenging at the same time. In this article, I would like to introduce you to some of the tools to build an intelligent chatbot.

    The title of the blog clearly tells that we have used Botkit and Rasa (NLU) to build our bot. Before getting into the technicalities, I would like to share the reason for choosing these two platforms and how they fit our use case. Also read – How to build a serverless chatbot with Amazon Lex.

    Bot development Framework — Howdy, Botkit and Microsoft (MS) Bot Framework were good contenders for this. Both these frameworks:
    – are open source
    – have integrations with popular messaging platforms like Slack, Facebook Messenger, Twilio etc
    – have good documentation
    – have an active developer community

    Due to compliance issues, we had chosen AWS to deploy all our services and we wanted the same with the bot as well.

    NLU (Natural Language Understanding) — API.ai (acquired by google) and Wit.ai (acquired by Facebook) are two popular NLU tools in the bot industry which we first considered for this task. Both the solutions:
    – are hosted as a cloud service
    – have Nodejs, Python SDK and a REST interface
    – have good documentation
    – support for state or contextual intents which makes it very easy to build a conversational platform on top of it.

    As stated before, we couldn’t use any of these hosted solutions due to compliance and that is where we came across an open source NLU called Rasa which was a perfect replacement for API.ai and Wit.ai and at the same time, we could host and manage it on AWS.

    You would now be wondering why I used the term NLU for Api.ai and Wit.ai and not NLP (Natural Language Processing). 
    * NLP refers to all the systems which handle the interactions with humans in the way humans find it natural. It means that we could converse with a system just the way we talk to other human beings. 
    * NLU is a subfield of NLP which handles a narrow but complex challenge of converting unstructured inputs into a structured form which a machine can understand and act upon. So when you say “Book a hotel for me in San Francisco on 20th April 2017”, the bot uses NLU to extract
    date=20th April 2017, location=San Francisco and action=book hotel
    which the system can understand.

    RASA NLU

    In this section, I would like to explain Rasa in detail and some terms used in NLP which you should be familiar with.
    * Intent: This tells us what the user would like to do. 
    Ex :  Raise a complaint, request for refund etc

    * Entities: These are the attributes which gives details about the user’s task. Ex — Complaint regarding service disruptions, refund cost etc

    * Confidence Score : This is a distance metric which indicates how closely the NLU could classify the result into the list of intents.

    Here is an example to help you understand the above mentioned terms — 
    Input: “My internet isn’t working since morning”.
        –  intent: 
          “service_interruption” 
         – entities: “service=internet”, 
          “duration=morning”.
         – confidence score: 0.84 (This could vary based on your training)

    NLU’s job (Rasa in our case) is to accept a sentence/statement and give us the intent, entities and a confidence score which could be used by our bot. Rasa basically provides a high level API over various NLP and ML libraries which does intent classification and entity extraction. These NLP and ML libraries are called as backend in Rasa which brings the intelligence in Rasa. These are some of the backends used with Rasa

    • MITIE — This is an all inclusive library meaning that it has NLP library for entity extraction as well as ML library for intent classification built into it.
    • spaCy + sklearn — spaCy is a NLP library which only does entity extraction. sklearn is used with spaCy to add ML capabilities for intent classification.
    • MITIE + sklearn — This uses best of both the worlds. This uses good entity recognition available in MITIE along with fast and good intent classification in sklearn.

    I have used MITIE backend to train Rasa. For the demo, I’ve taken a “Live Support ChatBot” which is trained for messages like this:
    * My phone isn’t working.
    * My phone isn’t turning on.
    * My phone crashed and isn’t working anymore.

    My training data looks like this:

    {
      "rasa_nlu_data": {
        "common_examples": [
        {
            "text": "hi",
            "intent": "greet",
            "entities": []
          },
          {
            "text": "my phone isn't turning on.",
            "intent": "device_failure",
            "entities": [
              {
                "start": 3,
                "end": 8,
                "value": "phone",
                "entity": "device"
              }
            ]
          },
          {
            "text": "my phone is not working.",
            "intent": "device_failure",
            "entities": [
              {
                "start": 3,
                "end": 8,
                "value": "phone",
                "entity": "device"
              }
            ]
          },
          {
            "text": "My phone crashed and isn’t working anymore.",
            "intent": "device_failure",
            "entities": [
              {
                "start": 3,
                "end": 8,
                "value": "phone",
                "entity": "device"
              }
            ]
          }
        ]
      }
    }

    NOTE — We have observed that MITIE gives better accuracy than spaCy + sklearn for a small training set but as you keep adding more intents, training on MITIE gets slower and slower. For a training set of 200+ examples with about 10–15 intents, MITIE takes about 35–45 minutes for us to train on a C4.4xlarge instance(16 cores, 30 GB RAM) on AWS.

    Botkit-Rasa Integration

    Botkit is an open source bot development framework designed by the creators of Howdy. It basically provides a set of tools for building bots on Facebook Messenger, Slack, Twilio, Kik and other popular platforms. They have also come up with an IDE for bot development called Botkit Studio. To summarize, Botkit is a tool which allows us to write the bot once and deploy it on multiple messaging platforms.

    Botkit also has a support for middleware which can be used to extend the functionality of botkit. Integrations with database, CRM, NLU and statistical tools are provided via middleware which makes the framework extensible. This design also allows us to easily add integrations with other tools and software by just writing middleware modules for them.

    I’ve integrated Slack and botkit for this demo. You can use this boilerplate template to setup botkit for Slack. We have extended Botkit-Rasa middleware which you can find here.

    Botkit-Rasa has 2 functions: receive and hears which override the default botkit behaviour.
    1. receive — This function is invoked when botkit receives a message. It sends the user’s message to Rasa and stores the intent and entities into the botkit message object.

    2. hears — This function overrides the default botkit hears method i.e controller.hears. The default hears method uses regex to search the given patterns in the user’s message while the hears method from Botkit-Rasa middleware searches for the intent.

    let Botkit = require('botkit');
    let rasa = require('./Middleware/rasa')({rasa_uri: 'http://localhost:5000'});
    
    let controller = Botkit.slackbot({
      clientId: process.env.clientId,
      clientSecret: process.env.clientSecret,
      scopes: ['bot'],
      json_file_store: __dirname + '/.db/'
    });
    
    // Override receive method in botkit
    controller.middleware.receive.use(rasa.receive);
    
    // Override hears method in botkit
    controller.changeEars(function (patterns, message) {
      return rasa.hears(patterns, message);
    });
    
    controller.setupWebserver(3000, function (err, webserver) {
      // Configure a route to receive webhooks from slack
      controller.createWebhookEndpoints(webserver);
    });

    Let’s try an example — my phone is not turning on”.
    Rasa will return the following
    1. Intent — device_failure
    2. Entites — device=phone

    If you notice carefully, the input I gave i.e my phone is not turning on” is a not present in my training file. Rasa has some intelligence built into it to identify the intent and entities correctly for such combinations. 

    We need to add a hears method listening to intent “device_failure” to process this input. Remember that intent and entities returned by Rasa will be stored in the message object by Rasa-Botkit middleware.

    let Botkit = require('botkit');
    let rasa = require('./Middleware/rasa')({rasa_uri: 'http://localhost:5000'});
    
    let controller = Botkit.slackbot({
      clientId: process.env.clientId,
      clientSecret: process.env.clientSecret,
      scopes: ['bot'],
      json_file_store: __dirname + '/.db/'
    });
    
    // Override receive method in botkit
    controller.middleware.receive.use(rasa.receive);
    
    // Override hears method in botkit
    controller.changeEars(function (patterns, message) {
      return rasa.hears(patterns, message);
    });
    
    controller.setupWebserver(3000, function (err, webserver) {
      // Configure a route to receive webhooks from slack
      controller.createWebhookEndpoints(webserver);
    });

    You should be able run this bot with slack and see the output as shown below (support_bot is the name of my bot).

    Conclusion

    You are now familiar with the process of building chatbots with a bot development framework and a NLU. Hope this helps you get started on your bot very quickly. If you have any suggestions, questions, feedback then tweet me @harjun1601. Keep following our blogs for more articles on bot development, ML and AI.

  • Automating test cases for text-messaging (SMS) feature of your application was never so easy

    Almost all the applications that you work on or deal with throughout the day use SMS (short messaging service) as an efficient and effective way to communicate with end users.

    Some very common use-cases include: 

    • Receiving an OTP for authenticating your login 
    • Getting deals from the likes of Flipkart and Amazon informing you regarding the latest sale.
    • Getting reminder notifications for the doctor’s appointment that you have
    • Getting details for your debit and credit transactions.

    The practical use cases for an SMS can be far-reaching. 

    Even though SMS integration forms an integral part of any application, due to the limitations and complexities involved in automating it via web automation tools like selenium, these are often neglected to be automated.

    Teams often opt for verifying these sets of test cases manually, which, even though is important in getting bugs earlier, it does pose some real-time challenges.

    Pitfalls with Manual Testing

    With these limitations, you obviously do not want your application sending faulty Text Messages after that major Release.

    Automation Testing … #theSaviour ‍

    To overcome the limitations of manual testing, delegating your task to a machine comes in handy.

    Now that we have talked about the WHY, we will look into HOW the feature can be automated.
    Technically, you shouldn’t / can’t use selenium to read the SMS via mobile.
    So, we were looking for a third-party library that is 

    • Easy to integrate with the existing code base
    • Supports a range of languages 
    • Does not involve highly complex codes and focuses on the problem at hand
    • Supports both incoming and outgoing messages

    After a lot of research, we settled with Twilio.

    In this article, we will look at an example of working with Twilio APIs to Read SMS and eventually using it to automate SMS flows.

    Twilio supports a bunch of different languages. For this article, we stuck with Node.js

    Account Setup

    Registration

    To start working with the service, you need to register.

    Once that is done, Twilio will prompt you with a bunch of simple questions to understand why you want to use their service.

    Twilio Dashboard

    A trial balance of $15.50 is received upon signing up for your usage. This can be used for sending and receiving text messages. A unique Account SID and Auth Token is also generated for your account.

    ‍Buy a Number


    Navigate to the buy a number link under Phone Numbers > Manage and purchase a number that you would eventually be using in your automation scripts for receiving text messages from the application.

    Note – for the free trial, Twilio does not support Indian Number (+91)

    Code Setup

    Install Twilio in your code base

     

    Code snippet

    For simplification,
    Just pass in the accountSid and authToken that you will receive from the Dashboard Console to the twilio library.This would return you with a client object containing the list of all the messages in your inbox.

    const accountSid = 'AC13fb4ed9a621140e19581a14472a75b0'
    const authToken = 'fac9498ac36ac29e8dae647d35624af7'
    const client = require('twilio')(accountSid, authToken)
    let messageBody
    let messageContent
    let sentFrom
    let sentTo
    let OTP
    describe('My Login application', () => {
      it('Read Text Message', () => {
        const username = $('#login_field');
        const pass = $('#password');
        const signInBtn = $('input[type="submit"]');
        const otpField = $('#otp');
        const verifyBtn = $(
          'form[action="/sessions/two-factor"] button[type="submit"]'
        );
        browser.url('https://github.com/login');
        username.setValue('your_email@mail.com');
        pass.setValue('your_pass123');
        signInBtn.click();
        // Get Message ...
        const latestMsg = await client.messages.list({ limit: 1 })
        
        messageContent = JSON.stringify(latestMsg,null,"\t")
        messageBody = JSON.stringify(latestMsg.body)
        sentFrom = JSON.stringify(latestMsg.from)
        sentTo = JSON.stringify(latestMsg.to)
        OTP = JSON.stringify(latestMsg.body.match(/\d+/)[0])
        otpField.setValue(OTP);
        verifyBtn.click();
        expect(browser).toHaveUrl('https://github.com/');
      });
    })

    List of other APIs to read an SMS provided by Twilio

    List all messages: Using this API Here you can see how to retrieve all messages from your account.

    const accountSid = process.env.TWILIO_ACCOUNT_SID;
    const authToken = process.env.TWILIO_AUTH_TOKEN;
    const client = require('twilio')(accountSid, authToken);
    
    client.messages.list({limit: 20})
                   .then(messages => messages.forEach(m => console.log(m.sid)));

    List Messages matching filter criteria: If you’d like to have Twilio narrow down this list of messages for you, you can do so by specifying a To number, From the number, and a DateSent.

    const accountSid = process.env.TWILIO_ACCOUNT_SID;
    const authToken = process.env.TWILIO_AUTH_TOKEN;
    const client = require('twilio')(accountSid, authToken);
    
    client.messages
          .list({
             dateSent: new Date(Date.UTC(2016, 7, 31, 0, 0, 0)),
             from: '+15017122661',
             to: '+15558675310',
             limit: 20
           })
          .then(messages => messages.forEach(m => console.log(m.sid)));

    Get a Message : If you know the message SID (i.e., the message’s unique identifier), then you can retrieve that specific message directly. Using this method, you can send emails without attachments.

    const accountSid = process.env.TWILIO_ACCOUNT_SID;
    const authToken = process.env.TWILIO_AUTH_TOKEN;
    const client = require('twilio')(accountSid, authToken);
    
    client.messages('MM800f449d0399ed014aae2bcc0cc2f2ec')
          .fetch()
          .then(message => console.log(message.to));

    Delete a message : If you want to delete a message from history, you can easily do so by deleting the Message instance resource.

    const accountSid = process.env.TWILIO_ACCOUNT_SID;
    const authToken = process.env.TWILIO_AUTH_TOKEN;
    const client = require('twilio')(accountSid, authToken);
    
    client.messages('MM800f449d0399ed014aae2bcc0cc2f2ec').remove();

    Limitations with a Trial Twilio Account

    • The trial version does not support Indian numbers (+91).
    • The trial version just provides an initial balance of $15.50.
      This is sufficient enough for your use case that involves only receiving messages on your Twilio number. But if the use case requires sending back the message from the Twilio number, a paid version can solve the purpose.
    • Messages sent via a short code (557766) are not received on the Twilio number.
      Only long codes are accepted in the trial version.
    • You can buy only a single number with the trial version. If purchasing multiple numbers is required, the user may have to switch to a paid version.

    Conclusion

    In a nutshell, we saw how important it is to thoroughly verify the SMS functionality of our application since it serves as one of the primary ways of communicating with the end users.
    We also saw what the limitations are with following the traditional manual testing approach and how automating SMS scenarios would help us deliver high-quality products.
    Finally, we demonstrated a feasible, efficient and easy-to-use way to Automate SMS test scenarios using Twilio APIs.

    Hope this was a useful read and that you will now be able to easily automate SMS scenarios.
    Happy testing… Do like and share …

  • How to build High-Performance Flutter Apps using Streams

    Performance is a significant factor for any mobile app, and multiple factors like architecture, logic, memory management, etc. cause low performance. When we develop an app in Flutter, the initial performance results are very good, but as development progresses, the negative effects of a bad codebase start showing up.  This blog is aimed at using an architecture that improves Flutter app performance. We will briefly touch base on the following points:

    1. What is High-Performance Architecture?

    1.1. Framework

    1.2. Motivation

    1.3. Implementation

    2. Sample Project

    3. Additional benefits

    4. Conclusion

    1. What is High-Performance Architecture?

    This Architecture uses streams instead of the variable-based state management approach. Streams are the most preferred approach for scenarios in which an app needs data in real-time. Even with these benefits, why are streams not the first choice for developers? One of the reasons is that streams are considered difficult and complicated, but that reputation is slightly overstated. 
    Dart is a programming language designed to have a reactive style system, i.e., architecture, with observable streams, as quoted by Flutter’s Director of Engineering, Eric Seidel in this podcast. [Note: The podcast’s audio is removed but an important part which is related to this architecture can be heard here in Zaiste’s youtube video.] 

    1.1. Framework: 

      Figure 01

    As shown in figure 01, we have 3 main components:

    • Supervisor: The Supervisor wraps the complete application, and is the Supervise responsible for creating the singleton of all managers as well as providing this manager’s singleton to the required screen.
    • Managers: Each Manager has its own initialized streams that any screen can access by accessing the respective singleton. These streams can hold data that we can use anywhere in the application. Plus, as we are using streams, any update in this data will be reflected everywhere at the same time.
    • Screens: Screens will be on the receiver end of this project. Each screen uses local streams for its operations, and if global action is required, then accesses streams from managers using a singleton.

    1.2. Motivation:

    Zaiste proposed an idea in 2019 and created a plugin for such architecture. He named it “Sprinkel Architecture” and his plugin is called sprinkle which made our development easy to a certain level. But as of today, his plugin does not support new null safety features introduced in Dart 2.12.0. You can check more about his implementation here and can try his given sample with following command:

    flutter run -–no-sound-null-safety

    1.3. Implementation:

    We will be using the get plugin and rxdart plugins in combination to create our high performance architecture.

    The Rxdart plugin will handle the stream creation and manipulation, whereas the get plugin can help us in dependency injection, route management, as well as state management.

    2. Sample Project:

    We will create a sample project to understand how to implement this architecture.

    2.1. Create a project using following command:

    flutter create sprinkle_architecture

    2.2. Add these under dependencies of pubspec.yaml (and run command flutter pub get):

    get: ^4.6.5
    Rxdart: ^0.27.4

    2.3. Create 3 directories, constants, managers, and views, inside the lib directory:

    2.4. First, we will start with a manager who will have streams & will increment the counter. Create dart file with name counter_manager.dart under managers directory:

    import 'package:get/get.dart';
    
    class CounterManager extends GetLifeCycle {
        final RxInt count = RxInt(0);
        int get getCounter => count.value;
        void increment() => count.value = count.value + 1;
    } 

    2.5. With this, we have a working manager. Next, we’ll create a Supervisor who will create a singleton of all available managers. In our case, we’ll create a singleton of only one manager. Create a supervisor.dart file in the lib directory:

    import 'package:get/get.dart';
    import 'package:sprinkle_architecture/managers/counter_manager.dart';
    
    abstract class Supervisor {
     static Future<void> init() async {
       _initManagers();
     }
    
     static void _initManagers() {
       Get.lazyPut<CounterManager>(() => CounterManager());
     }
    }

    2.6. This application only has 1 screen, but it is a good practice to create constants related to routing, so let’s add route details. Create a dart file route_paths.dart:

    abstract class RoutePaths {
      static const String counterPage = '/';
    }

    2.7. And route_pages.dart under constants directory:

    import 'package:get/get.dart';
    import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
    import 'package:sprinkle_architecture_exp/views/counter_page.dart';
    
    abstract class RoutePages {
     static final List<GetPage<dynamic>> pages = <GetPage<dynamic>>[
       GetPage<void>(
         name: RoutePaths.counterPage,
         page: () => const CounterPage(title: 'Flutter Demo Home Page'),
         binding: CounterPageBindings(),
       ),
     ];
    }
    
    class CounterPageBindings extends Bindings {
     @override
     void dependencies() => Get.lazyPut<CounterManager>(() => CounterManager());
    }

    2.8. Now, we have a routing constant that we can use. But do not have a CounterPage Class. But before creating this class, let’s update our main file:

    import 'package:flutter/material.dart';
    import 'package:get/get_navigation/src/root/get_material_app.dart';
    import 'package:sprinkle_architecture_exp/constants/route_pages.dart';
    import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
    import 'package:sprinkle_architecture_exp/supervisor.dart';
    
    void main() {
     WidgetsFlutterBinding.ensureInitialized();
     Supervisor.init();
     runApp(
       GetMaterialApp(
         initialRoute: RoutePaths.counterPage,
         getPages: RoutePages.pages,
       ),
     );
    }

    2.9. Finally, add the file counter_page_controller.dart:

    import 'package:get/get.dart';
    import 'package:sprinkle_architecture_exp/managers/counter_manager.dart';
    
    class CounterPageController extends GetxController {
     final CounterManager manager = Get.find();
    }

    2.10. As well as our landing page  counter_page.dart:

    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    import 'package:get/get.dart';
    import 'package:sprinkle_architecture_exp_2/views/counter_page_controller.dart';
    
    class CounterPage extends GetWidget<CounterPageController> {
     const CounterPage({Key? key, required this.title}) : super(key: key);
     final String title;
    
     CounterPageController get c => Get.put(CounterPageController());
    
     @override
     Widget build(BuildContext context) {
       return Obx(() {
         return Scaffold(
           appBar: AppBar(title: Text(title)),
           body: Center(
             child: Column(
               mainAxisAlignment: MainAxisAlignment.center,
               children: <Widget>[
                 const Text('You have pushed the button this many times:'),
                 Text('${c.manager.getCounter}',
                     style: Theme.of(context).textTheme.headline4),
               ],
             ),
           ),
           floatingActionButton: FloatingActionButton(
             onPressed: c.manager.increment,
             tooltip: 'Increment',
             child: const Icon(Icons.add),
           ),
         );
       });
     }
    }

    2.11. The get plugin allows us to add 1 controller per screen by using the GetxController class. In this controller, we can do operations whose scope is limited to our screen. Here, CounterPageController provides CounterPage the singleton on CounterManger.

    If everything is done as per the above commands, we will end up with the following tree structure:

    2.12. Now we can test our project by running the following command:

    flutter run

    3. Additional Benefits:

    3.1. Self Aware UI:

    As all managers in our application are using streams to share data, whenever a screen changes managers’ data, the second screens with dependency on that data also update themselves in real-time. This will happen because of the listen() property of streams. 

    3.2. Modularization:

    We have separate managers for handling REST APIs, preferences, appStateInfo, etc. So, the modularization happens automatically. Plus UI logic gets separate from business logic as we are using getXController provided by the get plugin

    3.3. Small rebuild footprint:

    By default, Flutter rebuilds the whole widget tree for updating the UI but with the get and rxdart plugins, only the dependent widget refreshes itself.

    4. Conclusion

    We can achieve good performance of a Flutter app with an appropriate architecture as discussed in this blog. 

  • Creating GraphQL APIs Using Elixir Phoenix and Absinthe

    Introduction

    GraphQL is a new hype in the Field of API technologies. We have been constructing and using REST API’s for quite some time now and started hearing about GraphQL recently. GraphQL is usually described as a frontend-directed API technology as it allows front-end developers to request data in a more simpler way than ever before. The objective of this query language is to formulate client applications formed on an instinctive and adjustable format, for portraying their data prerequisites as well as interactions.

    The Phoenix Framework is running on Elixir, which is built on top of Erlang. Elixir core strength is scaling and concurrency. Phoenix is a powerful and productive web framework that does not compromise speed and maintainability. Phoenix comes in with built-in support for web sockets, enabling you to build real-time apps.

    Prerequisites:

    1. Elixir & Erlang: Phoenix is built on top of these
    2. Phoenix Web Framework: Used for writing the server application. (It’s a well-unknown and lightweight framework in elixir) 
    3. Absinthe: GraphQL library written for Elixir used for writing queries and mutations.
    4. GraphiQL: Browser based GraphQL ide for testing your queries. Consider it similar to what Postman is used for testing REST APIs.

    Overview:

    The application we will be developing is a simple blog application written using Phoenix Framework with two schemas User and Post defined in Accounts and Blog resp. We will design the application to support API’s related to blog creation and management. Assuming you have Erlang, Elixir and mix installed.

    Where to Start:

    At first, we have to create a Phoenix web application using the following command:

    mix phx.new  --no-brunch --no-html

    –no-brunch – do not generate brunch files for static asset building. When choosing this option, you will need to manually handle JavaScript  dependencies if building HTML apps

    • –-no-html – do not generate HTML views.

    Note: As we are going to mostly work with API, we don’t need any web pages, HTML views and so the command args  and

    Dependencies:

    After we create the project, we need to add dependencies in mix.exs to make GraphQL available for the Phoenix application.

    defp deps do
    [
    {:absinthe, "~> 1.3.1"},
    {:absinthe_plug, "~> 1.3.0"},
    {:absinthe_ecto, "~> 0.1.3"}
    ]
    end

    Structuring the Application:

    We can used following components to design/structure our GraphQL application:

    1. GraphQL Schemas : This has to go inside lib/graphql_web/schema/schema.ex. The schema definitions your queries and mutations.
    2. Custom types: Your schema may include some custom properties which should be defined inside lib/graphql_web/schema/types.ex

    Resolvers: We have to write respective Resolver Function’s that handles the business logic and has to be mapped with respective query or mutation. Resolvers should be defined in their own files. We defined it inside lib/graphql/accounts/user_resolver.ex and lib/graphql/blog/post_resolver.ex folder.

    Also, we need to uppdate the router we have to be able to make queries using the GraphQL client in lib/graphql_web/router.ex and also have to create a GraphQL pipeline to route the API request which also goes inside lib/graphql_web/router.ex:

    pipeline :graphql do
    	  plug Graphql.Context  #custom plug written into lib/graphql_web/plug/context.ex folder
    end
    
    scope "/api" do
      pipe_through(:graphql)  #pipeline through which the request have to be routed
    
      forward("/",  Absinthe.Plug, schema: GraphqlWeb.Schema)
      forward("/graphiql", Absinthe.Plug.GraphiQL, schema: GraphqlWeb.Schema)
    end

    Writing GraphQL Queries:

    Lets write some graphql queries which can be considered to be equivalent to GET requests in REST. But before getting into queries lets take a look at GraphQL schema we defined and its equivalent resolver mapping:

    defmodule GraphqlWeb.Schema do
      use Absinthe.Schema
      import_types(GraphqlWeb.Schema.Types)
    
      query do
        field :blog_posts, list_of(:blog_post) do
          resolve(&Graphql.Blog.PostResolver.all/2)
        end
    
        field :blog_post, type: :blog_post do
          arg(:id, non_null(:id))
          resolve(&Graphql.Blog.PostResolver.find/2)
        end
    
        field :accounts_users, list_of(:accounts_user) do
          resolve(&Graphql.Accounts.UserResolver.all/2)
        end
    
        field :accounts_user, :accounts_user do
          arg(:email, non_null(:string))
          resolve(&Graphql.Accounts.UserResolver.find/2)
        end
      end
    end

    You can see above we have defined four queries in the schema. Lets pick a query and see what goes into it :

    field :accounts_user, :accounts_user do
    arg(:email, non_null(:string))
    resolve(&Graphql.Accounts.UserResolver.find/2)
    end

    Above, we have retrieved a particular user using his email address through Graphql query.

    1. arg(:, ): defines an non-null incoming string argument i.e user email for us.
    2. Graphql.Accounts.UserResolver.find/2 : the resolver function that is mapped via schema, which contains the core business logic for retrieving an user.
    3. Accounts_user : the custome defined type which is defined inside lib/graphql_web/schema/types.ex as follows:
    object :accounts_user do
    field(:id, :id)
    field(:name, :string)
    field(:email, :string)
    field(:posts, list_of(:blog_post), resolve: assoc(:blog_posts))
    end

    We need to write a separate resolver function for every query we define. Will go over the resolver function for accounts_user which is present in lib/graphql/accounts/user_resolver.ex file:

    defmodule Graphql.Accounts.UserResolver do
      alias Graphql.Accounts                    #import lib/graphql/accounts/accounts.ex as Accounts
    
      def all(_args, _info) do
        {:ok, Accounts.list_users()}
      end
    
      def find(%{email: email}, _info) do
        case Accounts.get_user_by_email(email) do
          nil -> {:error, "User email #{email} not found!"}
          user -> {:ok, user}
        end
      end
    end

    This function is used to list all users or retrieve a particular user using an email address. Let’s run it now using GraphiQL browser. You need to have the server running on port 4000. To start the Phoenix server use:

    mix deps.get #pulls all the dependencies
    mix deps.compile #compile your code
    mix phx.server #starts the phoenix server

    Let’s retrieve an user using his email address via query:

    Above, we have retrieved the id, email and name fields by executing accountsUser query with an email address. GraphQL also allow us to define variables which we will show later when writing different mutations.

    Let’s execute another query to list all blog posts that we have defined:

     Writing GraphQL Mutations:

    Let’s write some GraphQl mutations. If you have understood the way graphql queries are written mutations are much simpler and similar to queries and easy to understand. It is defined in the same form as queries with a resolver function. Different mutations we are gonna write are as follow:

    1. create_post:- create a new blog post
    2. update_post :- update a existing blog post
    3. delete_post:- delete an existing blog post

    The mutation looks as follows:

    defmodule GraphqlWeb.Schema do
      use Absinthe.Schema
      import_types(GraphqlWeb.Schema.Types)
    
      query do
        mutation do
          field :create_post, type: :blog_post do
            arg(:title, non_null(:string))
            arg(:body, non_null(:string))
            arg(:accounts_user_id, non_null(:id))
    
            resolve(&Graphql.Blog.PostResolver.create/2)
          end
    
          field :update_post, type: :blog_post do
            arg(:id, non_null(:id))
            arg(:post, :update_post_params)
    
            resolve(&Graphql.Blog.PostResolver.update/2)
          end
    
          field :delete_post, type: :blog_post do
            arg(:id, non_null(:id))
            resolve(&Graphql.Blog.PostResolver.delete/2)
          end
        end
    
      end
    end

    Let’s run some mutations to create a post in GraphQL:

    Notice the method is POST and not GET over here.

    Let’s dig into update mutation function :

    field :update_post, type: :blog_post do
    arg(:id, non_null(:id))
    arg(:post, :update_post_params)
    
    resolve(&Graphql.Blog.PostResolver.update/2)
    end

    Here, update post takes two arguments as input ,  non null id and a post parameter of type update_post_params that holds the input parameter values to update. The mutation is defined in lib/graphql_web/schema/schema.ex while the input parameter values are defined in lib/graphql_web/schema/types.ex —

    input_object :update_post_params do
    field(:title, :string)
    field(:body, :string)
    field(:accounts_user_id, :id)
    end

    The difference with previous type definitions is that it’s defined as input_object instead of object.

    The corresponding resolver function is defined as follows :

    def update(%{id: id, post: post_params}, _info) do
    case find(%{id: id}, _info) do
    {:ok, post} -> post |> Blog.update_post(post_params)
    {:error, _} -> {:error, "Post id #{id} not found"}
    end
    end

         

    Here we have defined a query parameter to specify the id of the blog post to be updated.

    Conclusion

    This is all you need, to write a basic GraphQL server for any Phoenix application using Absinthe.  

    References:

    1. https://www.howtographql.com/graphql-elixir/0-introduction/
    2. https://pragprog.com/book/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe
    3. https://itnext.io/graphql-with-elixir-phoenix-and-absinthe-6b0ffd260094