Author Archives: sachabarber

MADCAP IDEA PART 7 : REGISTRATION/LOGIN BACKEND

 

Last Time

 

So let me start with an apology that it has taken me so long to get this new post up since last time. I have snuck in a holiday and been on a training course which really only left me 1 week of train rides to do this in, lame excuse but there you have it.

Anyway last time we looked at doing all the react markup for the screens, this time we will be doing the backend registration and login functions which will make use of reactive mongo for the play side of things.

 

PreAmble

Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

 

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

So I think the best way to cover this post is just dive in to the 2 main topics covered

 

Registration

 

Passenger Registration 

 

As a reminder this is what the Passenger Registration screen looks like

 

image

 

And this is the relevant code that deals with the “Register” button being clicked

 

_handleValidSubmit = (values) => {
    var driver = values;
    var self = this;

    $.ajax({
        type: 'POST',
        url: 'registration/save/driver',
        data: JSON.stringify(driver),
        contentType: "application/json; charset=utf-8",
        dataType: 'json'
    })
    .done(function (jdata, textStatus, jqXHR) {
        var redactedDriver = driver;
        redactedDriver.password = "";
        console.log("redacted ${redactedDriver}");
        console.log(redactedDriver);
        console.log("Auth Service");
        console.log(self.props.authService);
        self.props.authService.storeUser(redactedDriver);
        self.setState(
            {
                okDialogHeaderText: 'Registration Successful',
                okDialogBodyText: 'You are now registered',
                okDialogOpen: true,
                okDialogKey: Math.random()
            });
    })
    .fail(function (jqXHR, textStatus, errorThrown) {
        self.setState(
            {
                okDialogHeaderText: 'Error',
                okDialogBodyText: jqXHR.responseText,
                okDialogOpen: true,
                okDialogKey: Math.random()
            });
    });
}

_okDialogCallBack = () => {
    this.setState(
        {
            okDialogOpen: false
        });
    if (this.state.wasSuccessful) {
        hashHistory.push('/');
    }
}

 

 

There are a couple of things of note there

  • That we use a standard POST that will post the registration data as JSON to the Play API backend code
  • That we also show a standard Boostrap OkDialog which we looked at last time, which when the Ok button is clicked will use the React Router to navigate to the route page

 

Let’s now turn our attention to the Play API backend code that goes with the “Passenger Registration”

 

JSON Read/Write support

The first thing we need to do is to set up support for reading JSON into Scala objects from a JSON string, and also allowing Scala objects to be turned back into a JSON string. For a “Passenger Registration” the Play API Json support comes via 3 Traits namely

  • Reads : Which allows reading a JSON string into a Scala object
  • Writes : Which allows a Scala object to be turned into a JSON string
  • Format : is just a mix of the Reads and Writes Traits and can be used for implicit conversion in place of its components.

The recommendation for both of these is that they are exposed as implicit vals you can read more about it here  : https://www.playframework.com/documentation/2.6.x/ScalaJsonCombinators

For the “Passenger Registration” it looks like this

package Entities

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class PassengerRegistration(
  fullName: String,
  email: String,
  password: String)

object PassengerRegistration {
  implicit val formatter = Json.format[PassengerRegistration]
}

object PassengerRegistrationJsonFormatters {

  implicit val passengerRegistrationWrites = new Writes[PassengerRegistration] {
    def writes(passengerRegistration: PassengerRegistration) = Json.obj(
      "fullname" -> passengerRegistration.fullName,
      "email" -> passengerRegistration.email,
      "password" -> passengerRegistration.password
    )
  }

  implicit val passengerRegistrationReads: Reads[PassengerRegistration] = (
    (JsPath \ "fullname").read[String] and
      (JsPath \ "email").read[String] and
      (JsPath \ "password").read[String]
    )(PassengerRegistration.apply _)
}

 

Once we have that in place we need to turn our attention to the actual endpoint to support the POST of a PassengerRegistration object. We first need to set up the route in the conf/routes file as follows:

 

# Registration page

POST  /registration/save/passenger             controllers.RegistrationController.savePassengerRegistration()

 

Reactive Mongo

Now that the route is in place its just a standard Play controller that we need. However I have chosen to use Reactive Mongo as my storage mechanism for registration/login data. This means we have a few things that we need to install

  1. Mongo itself which you can just grab from here : https://www.mongodb.com/download-center
  2. We then need to provision the actual Reactive Mongo Scala library, which we can do using the standard build.sbt file, where we specify the following dependency

 

libraryDependencies ++= Seq(
  "org.reactivemongo" %% "play2-reactivemongo" % "0.11.12"
)

 

Ensure Mongod.exe Is Running

In order to run the app we must ensure that the Mongo server is actually running which for my installation of Mongo means starting “Mongod.exe” from “C:\Program Files\MongoDB\Server\3.5\bin\mongod.exe” before I run the app

 

The Registration Controller

So now that we have talked about the JSON Reads/Writes and we know that we need Mongo downloaded and running, lets see what the actual controller looks like shall we. Here is the FULL code for the “Passenger Registration”

 

package controllers

import javax.inject.Inject
import play.api.mvc.{Action, Controller, Result}
import Entities._
import Entities.DriverRegistrationJsonFormatters._
import Entities.PassengerRegistrationJsonFormatters._
import scala.concurrent.{ExecutionContext, Future}
import play.modules.reactivemongo._
import play.api.Logger
import utils.Errors
import play.api.libs.json._
import reactivemongo.api.ReadPreference
import reactivemongo.play.json._
import collection._

class RegistrationController @Inject()(val reactiveMongoApi: ReactiveMongoApi)
  (implicit ec: ExecutionContext)
  extends Controller with MongoController with ReactiveMongoComponents {

  def passRegistrationFuture: Future[JSONCollection] = 
	database.map(_.collection[JSONCollection]("passenger-registrations"))

  def savePassengerRegistration = Action.async(parse.json) { request =>
    Json.fromJson[PassengerRegistration](request.body) match {
      case JsSuccess(newPassRegistration, _) =>

        //https://github.com/ReactiveMongo/ReactiveMongo-Extensions/blob/0.10.x/guide/dsl.md
        val query = Json.obj("email" -> Json.obj("$eq" -> newPassRegistration.email))

        dealWithRegistration[PassengerRegistration](
          newPassRegistration,
          passRegistrationFuture,
          query,
          PassengerRegistration.formatter)
      case JsError(errors) =>
        Future.successful(BadRequest(
			"Could not build a PassengerRegistration from the json provided. " +
			Errors.show(errors)))
    }
  }

  private def dealWithRegistration[T](
          incomingRegistration: T,
          jsonCollectionFuture: Future[JSONCollection],
          query: JsObject,
          formatter: OFormat[T])
          (implicit ec: ExecutionContext): Future[Result] = {

    def hasExistingRegistrationFuture = jsonCollectionFuture.flatMap {
        //http://reactivemongo.org/releases/0.11/documentation/advanced-topics/collection-api.html
        _.find(query)
        .cursor[JsObject](ReadPreference.primary)
        .collect[List]()
      }.map(_.length match {
          case 0 => false
          case _ => true
      }
    )

    hasExistingRegistrationFuture.flatMap {
      case false => {
        for {
          registrations <- jsonCollectionFuture
          writeResult <- registrations.insert(incomingRegistration)(formatter,ec)
        } yield {
          Logger.debug(s"Successfully inserted with LastError: $writeResult")
          Ok(Json.obj())
        }
      }
      case true => Future(BadRequest("Registration already exists"))
    }
  }
}

 

Lets break this down into chunks

 

  • The controller constructor
    • This takes a ReactiveMongoApi (this is mandatory to satisfy the base trait MongoController requirements)
    • Inherits from MongoController which provides a lot of use functionality
    • It also inherits from ReactiveMongoComponents in order to allow the cake pattern/self typing requirements of the base MongoController  which expects a ReactiveMongoComponents
  • The use of JSONCollection
    • There is a Future[JSONCollection] that represents the passenger collection in Mongo. This is a collection that stores JSON. When using reactive Mongo you have a choice about whether to use the standard BSON collections of JSON. I opted for JSON
  • The Guts Of The Logic
    • So now we have discussed the controller constructor and the Mongo collections. We just need to talk about the actual work that happens on registration. In a nutshell it works like this
    • The incoming JSON string is turned into a PassengerRegistration object via Play
    • We then create a new JSON query object to query the Mongo JSONCollection to see if a registration already exists
    • If a registration already exists we exit with a BadRequest output
    • If a registration does NOT already exist we insert the new registration details into the Mongo JSONCollection, and the we return an Ok output

 

And that is how the “Passenger Registration” works.

 

 

Driver Registration

The driver registration works in much the same way as described above, its just slightly different JSON, but it does share the same core logic/controller as the “Passenger Registration”

 

 

Login

Although the Login share some of the same ideas as Registration it has to do slightly different work, we will talk about what is different along the way, but this is what the Login screen looks like

image

 

And this is what the relevant code on the client looks like to send across the JSON payload.

_handleValidSubmit = (values) => {
    var logindetails = values;
    var self = this;

    $.ajax({
        type: 'POST',
        url: 'login/validate',
        data: JSON.stringify(logindetails),
        contentType: "application/json; charset=utf-8",
        dataType: 'json'
    })
    .done(function (jdata, textStatus, jqXHR) {

        console.log("result of login");
        console.log(jqXHR.responseText);
        let currentUser = jqXHR.responseText;
        self._authService.storeUser(currentUser);

        self.setState(
            {
                okDialogHeaderText: 'Login Successful',
                okDialogBodyText: 'You are now logged in',
                okDialogOpen: true,
                okDialogKey: Math.random()
            });
    })
    .fail(function (jqXHR, textStatus, errorThrown) {
        self.setState(
            {
                okDialogHeaderText: 'Error',
                okDialogBodyText: jqXHR.responseText,
                okDialogOpen: true,
                okDialogKey: Math.random()
            });
    });
}

 

What is different this time is that when the Login is successful we make use of an injected AuthService which we push out a true or false to indicate login status on a Rx subject, which all the other screens/components listen to, where each component can changes its own state/rendering depending on the login status. Here is the full code for the AuthService

 

import { injectable, inject } from "inversify";
import { TYPES } from "../types";
import Rx from 'rx';  

@injectable()
export class AuthService {

    private _isAuthenticated: boolean;
    private _authenticatedSubject = new Rx.Subject<boolean>();

    constructor() {

    }

    clearUser = () => {
        this._isAuthenticated = false;
        sessionStorage.removeItem('currentUserProfile');
        this._authenticatedSubject.onNext(false);
    }

    storeUser = (currentUser) => {
        this._isAuthenticated = true;
        sessionStorage.setItem('currentUserProfile', currentUser);
        this._authenticatedSubject.onNext(true);
    }

    userName = () => {
        var user = JSON.parse(sessionStorage.getItem('currentUserProfile'));
        return user.fullName;
    }

    isAuthenticated = () => {
        return this._isAuthenticated;
    }

    getAuthenticationStream = () => {
        return this._authenticatedSubject.asObservable();
    }
}

 

 

And here is example of how a React component may listen to this Rx subject to affect its own rendering

 

import * as React from "react";
import * as ReactDOM from "react-dom";

....
....

let authService = ContainerOperations.getInstance().container.get<AuthService>(TYPES.AuthService);

export interface MainNavProps {
    authService: AuthService;
}

export interface MainNavState {
    isLoggedIn: boolean;
}

class MainNav extends React.Component<MainNavProps, MainNavState> {

    private _subscription: any;


    constructor(props: any) {
        super(props);
        console.log(props);
        this.state = {
            isLoggedIn: false
        };
    }

    componentWillMount() {
        this._subscription = this.props.authService.getAuthenticationStream().subscribe(isAuthenticated => {
            this.state = {
                isLoggedIn: isAuthenticated
            };
            if (this.state.isLoggedIn) {
                hashHistory.push('/createjob');
            }
            else {
                hashHistory.push('/');
            }
        });
    }

    componentWillUnmount() {
        this._subscription.dispose();
    }

    render() {
       ....
       ....
    }
}


Now moving on to the server side Play API code, as before we need a Play backend route for the Login

 

# Login page

POST  /login/validate                          controllers.LoginController.validateLogin()

 

And as before we also have a Reactive Mongo enabled Play controller. I won’t go over the common stuff again, but here is the guts of the LoginController, and shown below is a bullet point list of what it does

 

package controllers

import javax.inject.Inject

import Entities.DriverRegistrationJsonFormatters._
import Entities.PassengerRegistrationJsonFormatters._
import Entities._
import play.api.Logger
import play.api.libs.json._
import play.api.mvc.{Action, Controller, Result}
import play.modules.reactivemongo._
import reactivemongo.api.ReadPreference
import reactivemongo.play.json._
import reactivemongo.play.json.collection._
import utils.Errors


import scala.concurrent.{ExecutionContext, Future}

class LoginController @Inject()(val reactiveMongoApi: ReactiveMongoApi)
                               (implicit ec: ExecutionContext)
  extends Controller with MongoController with ReactiveMongoComponents {

  def passRegistrationFuture: Future[JSONCollection] = database.map(_.collection[JSONCollection]("passenger-registrations"))
  def driverRegistrationFuture: Future[JSONCollection] = database.map(_.collection[JSONCollection]("driver-registrations"))


  def validateLogin = Action.async(parse.json) { request =>
    Json.fromJson[Login](request.body) match {
      case JsSuccess(newLoginDetails, _) =>
        newLoginDetails.isDriver match {
          case false => {
            val maybePassengerReg = extractExistingRegistration(
              passRegistrationFuture.flatMap {
                _.find(Json.obj("email" -> Json.obj("$eq" -> newLoginDetails.email))).
                  cursor[JsObject](ReadPreference.primary).
                  collect[List]()
              })
            returnRedactedRegistration[PassengerRegistration](
              maybePassengerReg,
              (reg: PassengerRegistration) => Ok(Json.toJson(reg.copy(password = "")))
            )

          }
          case true => {
            val maybeDriverReg = extractExistingRegistration(
              driverRegistrationFuture.flatMap {
                _.find(Json.obj("email" -> Json.obj("$eq" -> newLoginDetails.email))).
                  cursor[JsObject](ReadPreference.primary).
                  collect[List]()
              })
            returnRedactedRegistration[DriverRegistration](
              maybeDriverReg,
              (reg: DriverRegistration) => Ok(Json.toJson(reg.copy(password = "")))
            )
          }
        }
      case JsError(errors) =>
        Future.successful(BadRequest("Could not build a Login from the json provided. " +
          Errors.show(errors)))
    }
  }

  private def returnRedactedRegistration[T]
  (
    maybeDriverRegFuture: Future[Option[JsObject]],
    redactor : T => Result
  )(implicit reads: Reads[T]): Future[Result] = {
    maybeDriverRegFuture.map {
      case Some(json) => {
        val reg = Json.fromJson[T](json)
        reg match {
          case JsSuccess(reg, _) => {
            redactor(reg)
          }
          case _ => BadRequest("Registration already exists")
        }
      }
      case None => BadRequest("Could not find registration")
    }
  }

  private def extractExistingRegistration[T]
  (incomingRegistrations: Future[List[T]])
  (implicit writes: Writes[T], ec: ExecutionContext): Future[Option[T]] = {
    incomingRegistrations.map(matchedRegistrations =>
      matchedRegistrations.length match {
        case 0 => None
        case _ => Some(matchedRegistrations(0))
      }
    )
  }
}

 

Essentially the code above does the following:

 

  • Takes the deserialized JSON Login information, and decides whether it’s a Driver or a Passenger that is trying to login based on the IsDriver boolean.
  • Once we know if it’s a Driver or a Passenger that we are dealing with we continue to check if there is a registration for someone that has the login email
  • If we find a registered Driver or Passenger we obtain the registration that was previously stored and redact the password, and then convert it to JSON and send it back over the wire to the React code where it is stored in Local Storage and exposes out to the JS code using the AuthService.ts code above, and will notify any Rx subscribers of the login status, such that any listening React components can adjust their state/render information

 

 

Conclusion

I hope you have enjoyed this post, I know I have had fun with this one, I like the Reactive Mongo stuff, and I like how its all Future based which makes for a nice non-blocking workflow. We are now past the 1st phase of this project, the next phase will be to start with the Kafka messaging, where I may end up doing some more experimental/self contained code, rather than trying to shoe horn things into the main app in one go. I will still get it into the main app, but I am just thinking the Kafka posts may be better as stand alone things. I’ll see how it goes, at any rate the Kafka Streams stuff is phase 2, and we will be looking at that next

MADCAP IDEA PART 6 : STATIC SCREEN DESIGN

Last Time

 

Last time we looked at setting up the react router, which used some dummy screens. This time we will actually be implementing the static screen designs. The screens will be pretty much what the end application will look like, with the exception that they are not wired up to the Play framework backend, and for now might use dummy data to show what they would look like for real, when the entire app is completed and working

 

PreAmble

Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

 

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

So I think the best way to cover this post is to do it screen by screen, so lets start at the beginning and end at the end.

 

The Screens

In this section we will simply go through each of the screens and examine the code and the final rendering and discuss and important code that might be contained within the screen being discussed

 

Login

This is the entire code for the login screen

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput
} from "react-bootstrap";

import { Form, ValidatedInput } from 'react-bootstrap-validation';

import revalidator from 'revalidator';


let schema = {
    properties: {
        email: {
            type: 'string',
            maxLength: 255,
            format: 'email',
            required: true,
            allowEmpty: false
        },
        password: {
            type: 'string',
            minLength: 8,
            maxLength: 60,
            required: true,
            allowEmpty: false
        }
    }
};



export class Login extends React.Component<undefined, undefined> {
    render() {
        return (
            <Well className="outer-well">
                <Form
                    // Supply callbacks to both valid and invalid
                    // submit attempts
                    validateAll={this._validateForm}
                    onInvalidSubmit={this._handleInvalidSubmit}
                    onValidSubmit={this._handleValidSubmit}>
                    <Grid>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <h4>ENTER YOUR LOGIN DETAILS</h4>
                            </Col>
                        </Row>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <ValidatedInput type='text'
                                    label='Email'
                                    name='email'
                                    errorHelp='Email address is invalid'/>

                            </Col>
                        </Row>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <ValidatedInput type='password'
                                    name='password'
                                    label='Password'
                                    errorHelp='Password is invalid'/>

                            </Col>
                        </Row>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <ButtonInput
                                    id="loginBtn"
                                    type='submit'
                                    bsSize='small'
                                    bsStyle='primary'
                                    value='Register'>Login</ButtonInput>
                            </Col>
                        </Row>
                    </Grid>
                </Form>
            </Well>
        )
    }


    _validateForm = (values) => {
        let res = revalidator.validate(values, schema);

        // If the values passed validation, we return true
        if (res.valid) {
            return true;
        }

        // Otherwise we should return an object containing errors
        // e.g. { email: true, password: true }
        return res.errors.reduce((errors, error) => {
            // Set each property to either true or
            // a string error description
            errors[error.property] = true;

            return errors;
        }, {});
    }

    _handleInvalidSubmit = (errors, values) => {
        // Errors is an array containing input names
        // that failed to validate
        alert("Form has errors and may not be submitted");
    }

    _handleValidSubmit = (values) => {
        // Values is an object containing all values
        // from the inputs
        console.log("Form may be submitted");
        console.log(values);
    }
}

Where this will look like this in a browser

image

 

There are a couple of things to talk about on this screen.

 

Firstly there is the use of React-bootstrap components, which are all imported at the top of the file like this

 

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput
} from "react-bootstrap";

 

This allows us to import the actual React-bootstrap components and the CSS for bootstrap. React-bootstrap doesn’t really mind which CSS it uses, as long as it is a version of regular bootstrap CSS. I am using bootstrap v3.0 CSS here. React-bootstrap is more about the actual markup, and doing things in a more React like manner, to reduce some of the regular Bootstrap markup, which is possible thanks to using React.

 

The other important part about this screen is that it does client side validation. This is done using this library : react-bootstrap-validation. There are a few React validation libraries out there, but I like the simplicity and elegance of this one a lot. You really only have to do a couple of thing (as can be seen in full in the code above).

 

You need to import the library

 

import { Form, ValidatedInput } from 'react-bootstrap-validation';

import revalidator from 'revalidator';

 

Then you need a schema describing your form that you are trying to validate

let schema = {
    properties: {
        email: {
            type: 'string',
            maxLength: 255,
            format: 'email',
            required: true,
            allowEmpty: false
        },
        password: {
            type: 'string',
            minLength: 8,
            maxLength: 60,
            required: true,
            allowEmpty: false
        }
    }
};

 

And then you need to create the actual form (here is a stripped down one that matches the schema above)

 

<Form>
	<ValidatedInput type='text'
		label='Email'
		name='email'
		errorHelp='Email address is invalid'/>
	<ValidatedInput type='password'
		name='password'
		label='Password'
		errorHelp='Password is invalid'/>
</Form>

 

And we must also supply some validation callback hooks for the form, which are done like this

 

<Form
    // Supply callbacks to both valid and invalid
    // submit attempts
    validateAll={this._validateForm}
    onInvalidSubmit={this._handleInvalidSubmit}
    onValidSubmit={this._handleValidSubmit}>

</Form>

 

Where the code for these callbacks looks like this

 

export class Login extends React.Component<undefined, undefined> {
    render() {
        return (
          .....
        )
    }


    _validateForm = (values) => {
        let res = revalidator.validate(values, schema);

        // If the values passed validation, we return true
        if (res.valid) {
            return true;
        }

        // Otherwise we should return an object containing errors
        // e.g. { email: true, password: true }
        return res.errors.reduce((errors, error) => {
            // Set each property to either true or
            // a string error description
            errors[error.property] = true;

            return errors;
        }, {});
    }

    _handleInvalidSubmit = (errors, values) => {
        // Errors is an array containing input names
        // that failed to validate
        alert("Form has errors and may not be submitted");
    }

    _handleValidSubmit = (values) => {
        // Values is an object containing all values
        // from the inputs
        console.log("Form may be submitted");
        console.log(values);
    }
}

So that pretty much explains how the validation works, what does it look like when run? Well it looks like this when its not valid

 

image

 

And that is pretty much the static design of the Login screen, lets move on

 

 

Register

The Register screen uses the same validation stuff as above, so I will not cover it again. The only thing of note in the Register screen is that it will either need to render a Passenger registration or a Driver registration depending on what option was chosen by the user.

 

To do this conditional rendering, I opted for a set of 2 simple buttons that modify the internal state of the main Register React component, and will then either display a PassengerRegistration component of a DriverRegistration component.

 

Here is the full code for the main Register React component

 

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput,
    ButtonGroup,
    Button
} from "react-bootstrap";

import { PassengerRegistration } from "./PassengerRegistration";
import { DriverRegistration } from "./DriverRegistration";


export interface RegisterState {
    option: string;
}


export class Register extends React.Component<any, RegisterState> {

    constructor(props: any) {
        super(props);
        this.state = { option: "passenger" };
    }

    render() {
        return (
            <Well className="outer-well">
                <Grid>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>PLEASE ENTER YOUR REGISTRATION DETAILS</h4>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h6>Choose your registration type </h6>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ButtonGroup>
                                <Button bsSize='small' onClick={this._onOptionChange.bind(this, 'passenger') } active={this.state.option === 'passenger'}>Passenger</Button>
                                <Button bsSize='small' onClick={this._onOptionChange.bind(this, 'driver') } active={this.state.option === 'driver'}>Driver</Button>
                            </ButtonGroup>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            {this.state.option === 'passenger' ?
                                <div><PassengerRegistration/></div> :
                                <div><DriverRegistration/></div>
                            }
                        </Col>
                    </Row>
                </Grid>
            </Well>
        )
    }

    _onOptionChange(option) {
        this.setState({
            option: option
        });
    }

}

 

As you can see this code contains the state that is set to determine which type of sub registration component to actually render (see the Render() method above)

 

The sub components are as shown below (as stated above we already discussed validation, so I will not cover that again).

 

PassengerRegistration

Here is the full code for this sub component

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput
} from "react-bootstrap";

import { Form, ValidatedInput } from 'react-bootstrap-validation';

import revalidator from 'revalidator';


let schema = {
    properties: {
        fullname: {
            type: 'string',
            minLength: 8,
            maxLength: 12,
            required: true,
            allowEmpty: false
        },
        email: {
            type: 'string',
            maxLength: 255,
            format: 'email',
            required: true,
            allowEmpty: false
        },
        password: {
            type: 'string',
            minLength: 8,
            maxLength: 60,
            required: true,
            allowEmpty: false
        }
    }
};



export class PassengerRegistration extends React.Component<undefined, undefined> {
    render() {
        return (
            <Form className="submittable-form-inner"
                // Supply callbacks to both valid and invalid
                // submit attempts
                validateAll={this._validateForm}
                onInvalidSubmit={this._handleInvalidSubmit}
                onValidSubmit={this._handleValidSubmit}>
                <Grid>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>Passenger details</h4>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='FullName'
                                name='fullname'
                                errorHelp='FullName is invalid'/>

                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='Email'
                                name='email'
                                errorHelp='Email address is invalid'/>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='password'
                                label='Password'
                                name='password'
                                errorHelp='Password is invalid'/>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ButtonInput
                                id="registerBtn"
                                type='submit'
                                bsSize='small'
                                bsStyle='primary'
                                value='Register'>Register</ButtonInput>
                        </Col>
                    </Row>
                </Grid>
            </Form>
        )
    }

    _validateForm = (values) => {
        let res = revalidator.validate(values, schema);

        // If the values passed validation, we return true
        if (res.valid) {
            return true;
        }

        // Otherwise we should return an object containing errors
        // e.g. { email: true, password: true }
        return res.errors.reduce((errors, error) => {
            // Set each property to either true or
            // a string error description
            errors[error.property] = true;

            return errors;
        }, {});
    }

    _handleInvalidSubmit = (errors, values) => {
        // Errors is an array containing input names
        // that failed to validate
        alert("Form has errors and may not be submitted");
    }

    _handleValidSubmit = (values) => {
        // Values is an object containing all values
        // from the inputs
        console.log("Form may be submitted");
        console.log(values);
    }
}


 

DriverRegistration

Here is the full code for this sub component

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput
} from "react-bootstrap";

import { Form, ValidatedInput } from 'react-bootstrap-validation';

import revalidator from 'revalidator';


let schema = {
    properties: {
        fullname: {
            type: 'string',
            minLength: 8,
            maxLength: 12,
            required: true,
            allowEmpty: false
        },
        email: {
            type: 'string',
            maxLength: 255,
            format: 'email',
            required: true,
            allowEmpty: false
        },
        password: {
            type: 'string',
            minLength: 8,
            maxLength: 60,
            required: true,
            allowEmpty: false
        },
        vehicleDescription: {
            type: 'string',
            minLength: 6,
            maxLength: 60,
            required: true,
            allowEmpty: false
        },
        vehicleRegistrationNumber: {
            type: 'string',
            minLength: 6,
            maxLength: 30,
            required: true,
            allowEmpty: false
        }
    }
};



export class DriverRegistration extends React.Component<undefined, undefined> {
    render() {
        return (
            <Form className="submittable-form-inner"
                // Supply callbacks to both valid and invalid
                // submit attempts
                validateAll={this._validateForm}
                onInvalidSubmit={this._handleInvalidSubmit}
                onValidSubmit={this._handleValidSubmit}>
                <Grid>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>Driver details</h4>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='FullName'
                                name='fullname'
                                errorHelp='FullName is invalid'/>

                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='Email'
                                name='email'
                                errorHelp='Email address is invalid'/>

                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='password'
                                name='password'
                                label='Password'
                                errorHelp='Password is invalid'/>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>Vehicle details</h4>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='Vehicle Description'
                                name='vehicleDescription'
                                errorHelp='Vehicle description is invalid'/>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ValidatedInput type='text'
                                label='Vehicle Registration Number'
                                name='vehicleRegistrationNumber'
                                errorHelp='Vehicle registration number is invalid'/>
                        </Col>
                    </Row>

                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ButtonInput
                                id="registerBtn"
                                type='submit'
                                bsSize='small'
                                bsStyle='primary'
                                value='Register'>Register</ButtonInput>
                        </Col>
                    </Row>
                </Grid>
            </Form>
        )
    }


    _validateForm = (values) => {
        let res = revalidator.validate(values, schema);

        // If the values passed validation, we return true
        if (res.valid) {
            return true;
        }

        // Otherwise we should return an object containing errors
        // e.g. { email: true, password: true }
        return res.errors.reduce((errors, error) => {
            // Set each property to either true or
            // a string error description
            errors[error.property] = true;

            return errors;
        }, {});
    }

    _handleInvalidSubmit = (errors, values) => {
        // Errors is an array containing input names
        // that failed to validate
        alert("Form has errors and may not be submitted");
    }

    _handleValidSubmit = (values) => {
        // Values is an object containing all values
        // from the inputs
        console.log("Form may be submitted");
        console.log(values);
    }

}


And this is the end result, with the Passenger registration option selected.

 

image

 

 

And this is the driver registration

image

 

 

CreateJob

Ok so now things are getting slightly more exotic we need to use a Google map along with React. There are many good tutorials that walk you through this, which are great to read so you can see how the Google maps API gets wrapped (typically this is what is done) and you can also see how to use the React Ref attribute to grab the ACTUAL element from the real DOM to host the map. There are also many available react google maps components. I had a hunt around and tried out a few and settled on this one : https://github.com/tomchentw/react-google-maps where there are some great demos here : https://tomchentw.github.io/react-google-maps/

 

The job of the “CreateJob” screen for me was to allow the user to set their current position on a map, and issue the job out to drivers that may be logged in at that time.

 

Remember I will be doing all this work on my laptop. So that is my laptop pretending to be many drivers/users etc etc, so I cant really use the inbuilt GPS position, everyone would always be at the same place, ie where ever my laptop is at that moment.

 

Ok so now we know what the job of the “CreateJob” screen is lets have a look at some of the code.

 

For the React-Google-Maps we need this import, otherwise its just stuff you have seen before

 

import { withGoogleMap, GoogleMap, Marker, InfoBox } from "react-google-maps";

 

The next part of interest is how we create an actual map which might have a marker on the map with a custom icon. Thanks to the great work of the React-Google-Maps code, this is as simple as this

 

const CreateJobGoogleMap = withGoogleMap(props => (
    <GoogleMap
        ref={props.onMapLoad}
        defaultZoom={16}
        defaultCenter={{ lat: 50.8202949, lng: -0.1406958 }}
        onClick={props.onMapClick}>
        <Marker
            position={props.currentPosition}
            icon='/assets/images/passenger.png'>
        </Marker>
    </GoogleMap>
));

 

It can be seen there that the Marker is using the position that is passed down as props to it from the parent React component. Lets now have a look at the parent React component

 

export interface CreateJobState {
    currentPosition: any;
}

export class CreateJob extends React.Component<undefined, CreateJobState> {

    constructor(props: any) {
        super(props);
        this.state = {
            currentPosition: { lat: 50.8202949, lng: -0.1406958 }
          };
    }


    render() {
        return (
            <Well className="outer-well">
                <Grid>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>SET YOUR CURRENT LOCATION</h4>
                            <h6>Click the map to set your current location</h6>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <CreateJobGoogleMap
                                containerElement={
                                    <div style={{
                                        position: 'relative',
                                        top: 0,
                                        left: 0,
                                        right: 0,
                                        bottom: 0,
                                        width: 600,
                                        height: 600,
                                        justifyContent: 'flex-end',
                                        alignItems: 'center',
                                        marginTop: 20,
                                        marginLeft: 0,
                                        marginRight: 0,
                                        marginBottom: 20
                                    }} />
                                }
                                mapElement={
                                    <div style={{
                                        position: 'relative',
                                        top: 0,
                                        left: 0,
                                        right: 0,
                                        bottom: 0,
                                        width: 600,
                                        height: 600,
                                        marginTop: 20,
                                        marginLeft: 0,
                                        marginRight: 0,
                                        marginBottom: 20
                                    }} />
                                }
                                onMapLoad={this._handleMapLoad}
                                onMapClick={this._handleMapClick}
                                currentPosition={this.state.currentPosition}
                                />
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <ButtonInput
                            id="createJobBtn"
                            type='submit'
                            bsSize='small'
                            bsStyle='primary'
                            value='Register'>Create Job</ButtonInput>
                    </Row>
                </Grid>
            </Well>
        );
    }

    _handleMapLoad = (map) => {
        if (map) {
            console.log(map.getZoom());
        }
    }

    _handleMapClick = (event) => {
        this.setState({
            currentPosition: event.latLng
        });
    }
}

 

If you cut through all the bootstrap code to render things nicely it really boils down to what happens when the map gets clicked?

 

Well what happens is that the map getting clicked will fire the _handleMapClick function, which will set the internal currentPosition state value, which was handed down to the map sub component as props, so the Marker will be shown at the clicked location.

 

This is what the final rendering looks like

 

image

 

ViewJob

This is by far the most complex of all the screens, and here is why

  • It will have to show the incoming movements (geo-coords wise) of both driver(s) and passenger (icon distinguishes them from each other, as well as metadata)
  • A passenger should be able to accept a driver for a job
  • It will need to allow a job to be cancelled, with confirmation
  • It will need to allow a job to be completed
  • On completion of a job the driver should be able to rate a passenger
  • On completion of a job the passenger should be able to rate a driver

 

So as you can see it WILL do quite a lot, obviously a lot of this will come later once the streaming aspects of the code are completed. It is however quite a lot to do statically.

 

In fact it is so much that I have had to create 3 utility components to help me out

 

YesNoDialog

This represents a generic re-usable yes/no dialog that can be triggered, here is the code for this one. The important part is that the various prop values can be controlled via the parent component state values

 

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as _ from "lodash";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Button, 
    Modal
} from "react-bootstrap";


//TODO : Fix this
export interface YesNoDialogProps {
    headerText: string;
    theId: string;
    launchButtonText: string;
    yesCallBack: any;
    noCallBack: any;
}

export interface YesNoDialogState {
    showModal: boolean;
}


export class YesNoDialog extends React.Component<YesNoDialogProps, YesNoDialogState> {

    constructor(props) {
        super(props);
        console.log(this.props);
        //set initial state
        this.state = {
            showModal: false
        };
    }

    _yesClicked = () => {
        this.setState({ showModal: false });
        this.props.yesCallBack();
    }

    _noClicked = () => {
        this.setState({ showModal: false });
        this.props.noCallBack();
    }

    _close = () => {
        this.setState({ showModal: false });
    }

    _open = () => {
        this.setState({ showModal: true });
    }

    render() {
        return (
            <div className="leftFloat">

                <Button
                    id={this.props.theId}
                    type='button'
                    bsSize='small'
                    bsStyle='primary'
                    onClick={this._open}>{this.props.launchButtonText}</Button>

                <Modal show={this.state.showModal} onHide={this._close}>
                    <Modal.Header closeButton>
                        <Modal.Title>{ this.props.headerText }</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>
                        <h4>Are you sure?</h4>
                    </Modal.Body>
                    <Modal.Footer>
                        <Button
                            type='button'
                            bsSize='small'
                            bsStyle='primary'
                            onClick={this._yesClicked}>Yes</Button>
                        <Button
                            type='button'
                            bsSize='small'
                            bsStyle='danger'
                            onClick={this._noClicked}>Cancel</Button>
                    </Modal.Footer>
                </Modal>
            </div>
        );
    }
}

This looks like this when rendered

image

 

 

OkDialog

This represents a generic re-usable ok dialog that can be triggered, here is the code for this one. The important part is that the various prop values can be controlled via the parent component state values

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as _ from "lodash";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Button, 
    Modal
} from "react-bootstrap";


//TODO : Fix this
export interface OkDialogProps {
    headerText: string;
    bodyText: string;
    open: boolean;
    okCallBack: any;
}

export interface OkDialogState {
    showModal: boolean;
}


export class OkDialog extends React.Component<OkDialogProps, OkDialogState> {

    constructor(props) {
        super(props);
        console.log(this.props);
        //set initial state
        this.state = {
            showModal: false
        };
    }

    componentDidMount() {
        if (this.props.open === true) {
            this.setState({ showModal: true });
        }
    }

    _okClicked = () => {
        this.setState({ showModal: false });
        this.props.okCallBack();
    }

    _close = () => {
        this.setState({ showModal: false });
        this.props.okCallBack();
    }

    _open = () => {
        this.setState({ showModal: true });
    }

    render() {
        return (
            <div className="leftFloat">

                <Modal show={this.state.showModal} onHide={this._close}>
                    <Modal.Header closeButton>
                        <Modal.Title>{ this.props.headerText }</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>
                        <h4>{this.props.bodyText}</h4>
                    </Modal.Body>
                    <Modal.Footer>
                        <Button
                            type='button'
                            bsSize='small'
                            bsStyle='primary'
                            onClick={this._okClicked}>Ok</Button>
                    </Modal.Footer>
                </Modal>
            </div>
        );
    }
}

This looks like this when rendered

 

image

 

 

RatingDialog

This represents a generic re-usable rating control where rating can be from 1-5, here is the code for this one. The important part is that the various prop values can be controlled via the parent component state values

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as _ from "lodash";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Button, 
    Modal
} from "react-bootstrap";


import ReactStars from 'react-stars';


export interface RatingDialogProps {
    headerText: string;
    theId: string;
    okCallBack: any;
}

export interface RatingDialogState {
    showModal: boolean;
    rating: number;
}


export class RatingDialog extends React.Component<RatingDialogProps, RatingDialogState> {

    constructor(props) {
        super(props);
        console.log(this.props); 
        //set initial state
        this.state = {
            showModal: false,
            rating:0
        };
    }

    _close = () => {
        this.setState(
            {
                showModal: false,
                rating:0
            }
        );
    }

    _open = () => {
        this.setState(
            {
                showModal: true,
                rating: 0
            }
        );
    }

    _ratingChanged = (newRating) => {
        console.log(newRating)
        this.setState(
            {
                rating: newRating
            }
        );
    }

    _okClicked = () => {
        this._close();
        this.props.okCallBack();
    }

    render() {
        return (
            <div className="leftFloat">

                <Button
                    id={this.props.theId}
                    type='button'
                    bsSize='small'
                    bsStyle='primary'
                    onClick={this._open}>Complete</Button>

                <Modal show={this.state.showModal} onHide={this._close}>
                    <Modal.Header closeButton>
                        <Modal.Title>{ this.props.headerText }</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>
                        <h4>Give your rating between 1-5</h4>
                        <ReactStars count={5}
                                    onChange={this._ratingChanged}
                                    size={24}
                                    color2={'#ffd700'} />
                    </Modal.Body>
                    <Modal.Footer>
                        <Button
                            type='submit'
                            bsSize='small'
                            bsStyle='primary'
                            onClick={this._okClicked}>Ok</Button>
                    </Modal.Footer>
                </Modal>
            </div>
        );
    }
}

This looks like this when rendered

 

image

 

For the rating component I make use of this React library : https://www.npmjs.com/package/react-stars

 

 

So this just leaves the main component that represents the page, the full code is as follows:

 

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as _ from "lodash";

import { RatingDialog } from "./components/RatingDialog";
import { YesNoDialog } from "./components/YesNoDialog";
import { OkDialog } from "./components/OkDialog";


import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    ButtonInput,
    ButtonGroup,
    Button, 
    Modal,
    Popover,
    Tooltip,
    OverlayTrigger
} from "react-bootstrap";

import { withGoogleMap, GoogleMap, Marker, OverlayView } from "react-google-maps";

const STYLES = {
    overlayView: {
        background: `white`,
        border: `1px solid #ccc`,
        padding: 15,
    }
}


const GetPixelPositionOffset = (width, height) => {
    return { x: -(width / 2), y: -(height / 2) };
}



const ViewJobGoogleMap = withGoogleMap(props => (
    <GoogleMap
        ref={props.onMapLoad}
        defaultZoom={14}
        defaultCenter={{ lat: 50.8202949, lng: -0.1406958 }}>


        {props.markers.map((marker, index) => (
            <OverlayView
                key={marker.key}
                mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
                position={marker.position}
                getPixelPositionOffset={GetPixelPositionOffset}>
                <div style={STYLES.overlayView}>
                    <img src={marker.icon} />
                    <strong>{marker.key}</strong>
                    <br/>
                    <Button
                        type='button'
                        bsSize='xsmall'
                        bsStyle='primary'
                        onClick={() => props.onMarkerClick(marker) }
                        value='Accept'>Accept</Button>
                </div>
            </OverlayView>
        )) }
    </GoogleMap>
));

export interface ViewJobState {
    markers: any;
    okDialogOpen: boolean;
    okDialogKey: any;
    okDialogHeaderText: string;
    okDialogBodyText: string;
}

export class ViewJob extends React.Component<undefined, ViewJobState> {

    constructor(props: any) {
        super(props);
        this.state = {
            markers: [{
                    position: {
                        lat: 50.8202949,
                        lng: -0.1406958
                    },
                    key: 'driver_1',
                    icon: '/assets/images/driver.png'
                },
                {
                    position: {
                        lat: 50.8128187,
                        lng: -0.1361418
                    },
                    key: 'driver_2',
                    icon: '/assets/images/driver.png'
                }
            ],
            okDialogHeaderText: '',
            okDialogBodyText: '',
            okDialogOpen: false,
            okDialogKey:0
        };
    }


    render() {
        return (
            <Well className="outer-well">
                <Grid>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <h4>CURRENT JOB</h4>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <Col xs={10} md={6}>
                            <ViewJobGoogleMap
                                containerElement={
                                    <div style={{
                                        position: 'relative',
                                        top: 0,
                                        left: 0,
                                        right: 0,
                                        bottom: 0,
                                        width: 600,
                                        height: 600,
                                        justifyContent: 'flex-end',
                                        alignItems: 'center',
                                        marginTop: 20,
                                        marginLeft: 0,
                                        marginRight: 0,
                                        marginBottom: 20
                                    }} />
                                }
                                mapElement={
                                    <div style={{
                                        position: 'relative',
                                        top: 0,
                                        left: 0,
                                        right: 0,
                                        bottom: 0,
                                        width: 600,
                                        height: 600,
                                        marginTop: 20,
                                        marginLeft: 0,
                                        marginRight: 0,
                                        marginBottom: 20
                                    }} />
                                }
                                markers={this.state.markers}
                                onMarkerClick={this._handleClick}/>
                        </Col>
                    </Row>
                    <Row className="show-grid">
                        <span>
                            <RatingDialog
                                theId="viewJobCompleteBtn"
                                headerText="Rate your driver/passenger"
                                okCallBack= {this._ratingsDialogOkCallBack}/>

                            <YesNoDialog
                                theId="viewJobCancelBtn"
                                launchButtonText="Cancel"
                                yesCallBack={this._jobCancelledCallBack}
                                noCallBack={this._jobNotCancelledCallBack}
                                headerText="Cancel the job"/>

                            <OkDialog
                                open= {this.state.okDialogOpen}
                                okCallBack= {this._okDialogCallBack}
                                headerText={this.state.okDialogHeaderText}
                                bodyText={this.state.okDialogBodyText}
                                key={this.state.okDialogKey}/>
                        </span>
                    </Row>
                </Grid>
            </Well>
        );
    }

    _handleClick = (targetMarker) => {
        console.log('button on overlay clicked:' + targetMarker.key);
    }

    _ratingsDialogOkCallBack = () => {
        console.log('RATINGS OK CLICKED');
        this.setState(
            {
                okDialogHeaderText: 'Ratings',
                okDialogBodyText: 'Rating successfully recorded',
                okDialogOpen: true,
                okDialogKey: Math.random()
            });
    }

    _jobCancelledCallBack = () => {
        console.log('YES CLICKED');
        this.setState(
            {
                okDialogHeaderText: 'Job Cancellaton',
                okDialogBodyText: 'Job successfully cancelled',
                okDialogOpen: true,
                okDialogKey: Math.random() 
            });
    }

    _jobNotCancelledCallBack = () => {
        console.log('NO CLICKED');
        this.setState(
            {
                okDialogHeaderText: 'Job Cancellaton',
                okDialogBodyText: 'Job remains open',
                okDialogOpen: true,
                okDialogKey: Math.random() 
            });
    }

    _okDialogCallBack = () => {
        console.log('OK on OkDialog CLICKED');
        this.setState(
            {
                okDialogOpen: false
            });
    }
}

 

Which makes use of some of the React-Google-Maps goodness we saw before, but instead of Marker objects we are now using Overlay objects to represent the driver/passenger info, obviously we make use of an array of items this time, as many driver(s) might bid for a job. Also shown here is how you can create the dialogs on demand and have their content tailored to the specific scenario in hand.

 

Right now when this component renders it looks like this

 

image

 

ViewRating

The Rating screen is perhaps the easiest of all the screens it simply allows you view your overall rating and who made the rating for you so far. This would like be an active query over a Kafka streams KTable.

 

Here is the full code for this screen, there is not much to say on this one

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import
{
    Well,
    Grid,
    Row,
    Col,
    Label
} from "react-bootstrap";


export class ViewRating extends React.Component<undefined, undefined> {
    render() {
        return (
            <Well className="outer-well">
                    <Grid>
                        <Row className="show-grid">
                            <Col xs={6} md={6}>
                                <div>
                                    <h4>YOUR RANKING <Label>4.2</Label></h4>
                                </div>
                            </Col>
                        </Row>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <h6>The finer details of your ranking are shown below</h6>
                            </Col>
                        </Row>
                        <Row className="show-grid">
                            <Col xs={10} md={6}>
                                <div className="table-responsive">
                                    <table className="table table-striped table-bordered table-condensed factTable">
                                        <thead>
                                            <tr>
                                                <th>Ranked By</th>
                                                <th>Rank Given</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <tr>
                                                <td>John Doe</td>
                                                <td>4.2</td>
                                            </tr>
                                            <tr>
                                                <td>Mary Moe</td>
                                                <td>4.7</td>
                                            </tr>
                                            <tr>
                                                <td>July Dooley</td>
                                                <td>4.5</td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </Col>
                        </Row>
                    </Grid>
            </Well>
        )
    }
}


 

When rendered it looks like this

 

image

 

 

Conclusion

I am no web designed, but I am fairly pleased with the outcome of the static screen designs, and have found using React-bootstrap/react/the map components and the React-validation to be quite simple and they all seemed to play together quite nicely.

MADCAP IDEA PART 5 : ADDING REACT-ROUTER

 

Last Time

 

Last time we looked at using Balsamiq mockups to prototype our screens. This post is a small one, but one that I have found slightly trickier to get to work. This time we will look at routing inside of a React application.

 

PreAmble

Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

 

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

What is routing?

Routing is the act of taking a uri (with our without parameters), and finding a resource (be that a page, react component file) that matches the given uri and returning that content to be rendered.

 

What choices do we have?

When it comes to React there are not than many choices at all. We need to either hand roll our own routing mechanism which might be accomplished either using

window.location.hash
window.location.href

This is however quite a pain, and you would need to hand roll your own support for richer routes that requires route parameters such as /orders/{orderId}. So perhaps there is a better way out there already. There is of course, the https://reacttraining.com/react-router/

 

 

 

React-Router

This the defacto go to solution for routing in React. The react-router was built with react in mind, which means at its heart it is a component based router. It is quite stable and been around quite a while, The current version is v4, which is quite a different beast from the previous versions, a lot changed. So for this series of posts, and the code that goes with it, I will be using v3 of the react-router.

 

Installing react-router

To install react-router, you just need to use NPM (though this article has used a specific version of react-router)

npm install react-router --save

 

Creating the components

As I say, at its heart the react-router is a component based router. So we need some components to render, route to. For the demo all my routes are simple routes that don’t require any further parameters. But rest assure the react-router can deal with routing parameters just fine.

 

So here are the demo route components that we would like to route to.

 

class ReDirecter extends React.Component<undefined, undefined> {

    handleClick = () => {
        hashHistory.push('/contact');
    };

    render() {
        return (
             <button onClick={this.handleClick} type="button">go to contact</button>

        )
    }
}



const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
)

const Contact = () => (
  <div>
    <h2>Contact</h2>
  </div>
)


const About = () => (
  <div>
    <h2>About</h2>
  </div>
)

 

It can be seen that these are very simple components, but they are good enough to route to. There is also an example above of how to use the router to navigate to a new route programmatically in code, but we will talk more about this later.

 

Types of history

So now that we have some components that can be routed to, we need to just understand a bit more about history, and what we get out of the box with react-router. There is great page describing this here : https://github.com/ReactTraining/react-router/blob/v2.0.0-rc5/docs/guides/basics/Histories.md

 

The real crux of it is that there are several types of history providers that can be used with the router

 

hashHistory

Which is a simple hash history which DOES NOT need server side route support.

 

browserHistory

This type of history DOES require FULL server side route support. Which means you really need to think about how your components need to be split up and served from the server side code. This history provider does support isomorphic rendering though.

 

For me the hashHistory above was what I wanted, so I have used that.

 

 

Creating the router

Ok so now that we have talked about the types of history and talked about having components to route to, how we create the router?

 

Well quite simply we do it like this (remember this is v3 of the router, v4 may be different). Note that I am using ES6 style imports

 

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import {Nav, Navbar, NavItem, NavDropdown, MenuItem} from "react-bootstrap";

import { Router, Route, hashHistory  } from 'react-router'





ReactDOM.render((
    <Router history={hashHistory}>
            <Route component={App}>
                <Route path="/" component={Home}/>
                <Route path="/contact" component={Contact}/>
                <Route path="/about" component={About}/>
                <Route path="/redirecter" component={ReDirecter}/>
            </Route>
        </Router>
), document.getElementById('root'));

 

It can be seen that the router itself is a React component and gets rendered to a target DOM element. It can also be seen that we include a hashHistory which we imported from react-router.  Each route is added as a new Route where we match the expected route path, with the component to render.

 

I will also be showing you how to use the react-router with a bootstrap-react Navbar too, so there are imports for that too.

 

 
How does that work with a bootstrap-react navbar?

So I just mentioned I would like to use a bootstrap-react Navbar, so what does that look like. Well some of the eagle eyed amongst you may have noticed the line component={App} in the router setup we just saw. Lets have a look at that.

 

import * as React from "react";
import * as ReactDOM from "react-dom";

import 'bootstrap/dist/css/bootstrap.css';
import {Nav, Navbar, NavItem, NavDropdown, MenuItem} from "react-bootstrap";

import { Router, Route, hashHistory  } from 'react-router'


class MainNav extends React.Component<undefined, undefined> {
  render() {
    return (
     <Navbar brand='React-Bootstrap'>
         <Nav>
             <NavItem eventKey={1} href='#/'>Home</NavItem>
             <NavItem eventKey={2} href='#/contact'>Contact</NavItem>
             <NavItem eventKey={2} href='#/about'>About</NavItem>
             <NavItem eventKey={2} href='#/redirecter'>Redirect</NavItem>
         </Nav>
     </Navbar>
    )
  }
}



class App extends React.Component<undefined, undefined> {
  render() {
    return (

        <div>
            <MainNav/>
            {this.props.children}
        </div>
    )
  }
}

 

It can be seen that the App component is another React component that does 2 things:

  • Renders another component, namely MainNav (which is a bootstrap-react Navbar component, which is also shown above, where the Navbar has links  for the required routes)
  • Also renders the children of the router, which for this example will be a single component (so for the demo code will be one of ReDirecter / Home / Contact / About components)
  •  
 
What about on the fly route changes?

But what about when we are inside of a React component and we may wish to navigate somewhere else, how do we do that? Well it is done via the use of the routers history provider (hashHistory / browserHistory etc etc), here in an example of that from a simple React component that shows a button, which when clicked will perform a redirect to the Contact component, the trick is to use the history provider push(..) method to set a new route location

 

class ReDirecter extends React.Component<undefined, undefined> {

    handleClick = () => {
        hashHistory.push('/contact');
    };

    render() {
        return (
             <button onClick={this.handleClick} type="button">go to contact</button>

        )
    }
}

 

 

Demo

Ok so now we have covered all the basics, lets run the code up and see what we get.

 

When we first start we see this, where we can see the Navbar, and the address bar contains a # style address (this is due to using the hashHistory history provider for the Router)

 

image

 

We can click on the various links representing the routes, and the relevant component will get rendered by the Router. The Redirect route is shown below, where clicking the button will perform a redirect to the Contact component

 

image

 

 

Conclusion

As you can see even something as page navigation in React requires a bit of thought, and depending on what type of routing you chose to use, may even require a fair amount of server side work, which could change the entire workflow quite a bit.

 

I went for the hashHistory for this demo but for a real world production ready application, you should use the browserHistory, and ensure you have supporting server side code to deal with the expected routing requests.

MADCAP IDEA PART 4 : PROTOTYPING THE SCREENS

 

Last Time

 

Last time we looked at bringing in the Play Framework (scala based MVC web framework) and making the front end work with Play. This time we will  be look at the initial prototypes of the screens.

This is my best guess of what they may look like right now, based on my initial requirements, but as with all things once you get into the guts of it, changes will occur.

 

 

PreAmble

Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

Mockup tool of choice

I am a big fan of the balsamiq mockup tools https://balsamiq.com/. This comes as a stand alone installed version or as a plugin for JIRA.

balsamiq provides the following (I am just listing the features I used, there are many more)

 

  • Drag and drop from a wide range of forms, containers, controls
  • Set content for controls (usually using some fancy design time behavior)
  • Set navigation links
  • Set properties like IsSelected, IsEnabled etc etc

 

This is what the windows installed balsamiq desktop version looks like, see how you have many categories of items to choose from

 

image

 

And here is what I mean by the clever design time support. This is a data grid that I have double clicked on, where the text in design mode described the rendered results of the control

 

image

 

It really is a very nice tool. Anyway on with the initial screen designs

 

Navbar

image

 

This will be a simple react-router / react-bootstrap based navigation bar. There is nothing much more to say about that.

 

 

Login

image

 

This will be a login form which will be validated, and submitted to a Play framework controller, for further validation. The Play controller would look up the user details from a MySQL database, and if an entry is found the user is considered logged on. Keeping in simple here no oAuth no JWT, just simple lookup

 

Passenger Register

image

 

If the user is a passenger the sort of information that they will need to enter to register will be different from a driver who may need to register, as such there is a specific passenger registration form, which will be validated and sent to a Play controller endpoint for storage in MySQL.

 

Driver Register

image

 

If the user is a driver, we need more information about the vehicle, as such there is a specific driver registration form, which will be validated and sent to a Play controller endpoint for storage in MySQL.

 

 

Create Job

image

 

Only a passenger will be able to create new jobs. Since I am doing all this work on a single laptop which is ALWAYS in a single location, I am having to SIMULATE the geo-coordinates of a job by accepting the current users input for their current position. The passenger/driver users will provide this geo information by clicking on a google map. The geo co-ordinate update will either travel through a Kafka stream, –> Akka –> Comet, or may just use Akka –> Comet. I have not fully decided on this part yet.

 

There may only EVER be 1 active job, so if a logged in passenger tries to create a 2nd job this should cause an error

 

image

 

View Job

Both passengers/drivers may view an active job. Drivers may “bid for a job” by clicking on the map providing the job is not already paired with a driver.  A driver symbol will be a car, as before the driver will update their geo co-ordinates by clicking on the map. A before the geo co-ordinate update will either travel through a Kafka stream, –> Akka –> Comet, or may just use Akka –> Comet.

 

image

 

A passenger may inspect a drivers details, and chose to accept the driver, at which point the passengers job become assigned the chosen driver.

 

image

 

Drivers that are not allocated to the job will be removed from the map, and only geo updates from the paired passenger/driver will be reflected on the map. 

 

Passenger Completion

 

image

Once a job has been completed (by clicking the”Complete” button) the passenger will be able to rank the driver. This will store the ranking for the driver. This could be stored directly in MySQL, but I want to play with Kafka Streams a bit more, so we use a Kafka Publisher –> Kafka Streams –> KTable arrangement to store the state. And then use Kafka active queries to get the data out again.

 

 

Driver Completion

image

 

The driver is also able to complete the job from their end (using the “complete” button), and is able to rank the passenger. This will work as described above.

 

View Ranking

image

 

Depending on which way I go with the ranking storage this will either be a direct MySQL query or a Kafka Streams active query over a KTable.

 

Conclusion

This is perhaps the simplest of all the posts in this series, but it is an important one. Next time we will try and statically implement these screens, and the associated routing that goes with them.

Madcap Idea Part 3 : Bringing Play Back End Into The Fold + Some Basic Streaming

Last Time

Last time we looked at bringing in the Inversify.js JS IOC container. This time we will bring a Play Framework (scala based MVC web framework) into the fold, and shall be using the front end we have been working on so far to be the front end for the Play Framework back end.

 

PreAmble

Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

What Is The Play Framework?

The Play Framework is a Scala based MVC (model view controller) type web application framework. As such it has in built mechanisms for things typical of a MVC web framework (certainly if you have done any ASP MVC . NET you would find it very familiar).

So we have the typical MVC concerns covered by the Play Framework

  • Controllers
  • Actions
  • Routing
  • Model binding
  • JSON support
  • View engine

Thing is I will not be doing any actual HTML in the Play Framework back end code, I want to do all of that using the previously covered webpack/typescript/react starter code I have shown so far. Rather I will be using the Play Framework as a API backend, where we will simply be using various controllers as endpoint to accept/serve JSON, and Event streamed data. All the actual front end work/routing will be done via webpack and React.

 

There are still some very appealing parts in Play that I did want to make use of, such as:

  • It is Scala, which means when I come to integrate Kafka / Kafka Streams it will be ready to do so
  • It uses Akka which I wanted to use. I also want to use Akka streams, which Play also supports
  • Play controllers lend themselves quite nicely to being able to create a fairly simple REST API
  • It can be used fairly easily to serve static files (think of these as the final artifacts that come out of the webpack generation pipeline). So things like minimized CSS / JS etc etc

 

So hopefully you can see that using Play Framework still made a lot of sense, even if we will only end up using 1/2 of what it has to offer. To be honest the idea of using controllers for a REST API is something that is done in ASP MVC .NET all time either by using of actual controllers or by using the WebApi.

Ok so now that we know what we will be using Play Framework for, how about we dive into the code for this post.

 

Play Framework Basics

Lets start by looking at the bare bones structure of a Play Framework application, which looks like this (I am using IntelliJ IDEA as my IDE)

image

Lets talk a bit about each of these folders

 

app

This folder would hold controllers/views (I have added the Entities folder there that is not part of a Play Framework application requirements). Inside the controllers folder you would find controllers, and inside the views folder you would find views. For the final app there will be no views folder, I simply kept that in this screenshot to talk about what a standard Play Framework application looks like

 

conf

This folder contains the configuration for the Play Framework application. This would include the special routes file, and any other application specific configuration would might have.

Lets just spend a minute having a look at the Play Framework routes file, which you can read more about here : https://www.playframework.com/documentation/2.5.x/ScalaRouting

The routes file has its own DSL, that is responsible for matching a give route with a controller + controller action. The controller action that matches the route is ultimately responsible for servicing the http request. I think the DSL shown in routes file below is pretty self explanatory with perhaps the exception of the assets based routes.

 

All assets based http requests (ie ones that start with /assets for example http://localhost:9000/assets/images/favicon.png would actually be routed through to a special controller called Assets. You dont see any code for this one, its part of the Play Framework application codebase. This special Assets inbuilt play controller is responsible for serving up static data files which it expects to find in the public folder. So for example our initial request of http://localhost:9000/assets/images/favicon.png would get translated into this file (relative path from project root) /public/images/favicon.png. As I say this is handled for you by the special Assets built in controller.

 

The only other funky part to the Assets based route is that it uses a *file in its route. Which essentially boils down to the play framework being able match a multi-part path. Which we actually just saw with the example above http://localhost:9000/assets/images/favicon.png , see how that contains not only the file name, but also a directory of images. The Assets controller + routing is able to deal with that path just fine.

 

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page

GET        /                                   controllers.HomeController.index()

GET        /scala/comet/liveClock              controllers.ScalaCometController.streamClock()
GET        /scala/comet/kick                   controllers.ScalaCometController.kickRandomTime()

# Map static resources from the /public folder to the /assets URL path
GET        /assets/*file                       controllers.Assets.at(path="/public", file)

 

Ok so moving on to the rest of the standard folders that come with a Play Framework application

 

public

This is where you will need to put any static content that you wish to be served. Obviously views (if you use that part of play) will be within in the app/views folder. Like I say I am not using the views aspect of Play so you will not be seeing any views in my views folder. I instead want to let webpack et all generate my routing, web page etc etc. I do however want to serve bundles so later on I will be showing you how my webpack generated bundles fit in with the Play Framework ecco system.

 

target

Since this is a scala based project we get the standard scala based folders, and target is one of them, that has the generated/compiled code in it.

 

SBT

It is worth pointing out that my Play Framework application is an SBT based project, as such there is an SBT aspect to it, which largely boils down to these files

 

Project [root-build] / plugs.sbt file

This file adds Play as a plugin for the SBT project

 

// The Lightbend repository
resolvers += Resolver.typesafeRepo("releases")

// Use the Play sbt plugin for Play projects
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.14")

 

build.sbt

This is the main SBT build file for the project. This is where all our external dependencies are brought in etc etc (standard SBT stuff)

 

import play.sbt._
import sbt.Keys._
import sbt._

name := "play-streaming-scala"

version := "1.0-SNAPSHOT"

scalaVersion := "2.11.11"

lazy val root = (project in file(".")).enablePlugins(play.sbt.PlayScala)

javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-Xlint")

initialize := {
  val _ = initialize.value
  if (sys.props("java.specification.version") != "1.8")
    sys.error("Java 8 is required for this project.")
}

 

So I think that covers the basics of a standard Play Framework application, the remainder of this post will look at the following aspects

  • How do we provide a stream based request, that allows server sent events to be sent back to JavaScript from server side code
  • How do we integrate the webpack based front end we have been playing with in previous posts

 

How do provide a stream based request (server sent events)

One of the main ingredients of the application we have chosen to build is the ability to stream data back to the JavaScript in response to incoming Kafka data. Ok we have not got to the Kafka part yet, but we know we will need some way of pushing data from the Play Framework application code to the front end JavaScript code. So how do we do that?

Well there are several parts to this, so lets just go through them in turn

 

Play streaming route

There are 2 routes for the example stream for this post

  • The actual stream endpoint route itself
  • An endpoint that allows us to produce a new value for the Akka Stream that is exposes in the stream endpoint route

Here is the full routing code for these 2 routes

 

GET        /scala/comet/liveClock              controllers.ScalaCometController.streamClock()
GET        /scala/comet/kick                   controllers.ScalaCometController.kickRandomTime()

 

Play stream provider

The job of creating the stream is handled by the ScalaCometController where it uses Akka Streams to provide the stream itself. The clever part is what parts of the Akka Streams API we used.

I have chosen to use MergeHub and BroadCastHub, which act as Fan-In and Fan-Out stages respectively. This allows us to have many producers and many consumers that are able to push and listen to the stream.

 

I have also supplied a route kickRandomTime() which will produce a new random JSON value that will either be a Location or a Resident domain object entity converted to JSON.

Another thing to note is how we handle stream errors. We use ideas borrowed from regular Akka where we use a strategy to manage the stream

Here is the full code to the ScalaCometController

 

package controllers

import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import javax.inject.{Inject, Singleton}

import akka.actor.ActorSystem
import akka.stream.{ActorMaterializer, ActorMaterializerSettings, Materializer, Supervision}
import akka.stream.scaladsl.{BroadcastHub, Keep, MergeHub, Source}
import play.api.http.ContentTypes
import play.api.libs.Comet
import play.api.libs.json._
import play.api.mvc.{Controller, _}
import Entities._
import Entities.JsonFormatters._

import scala.concurrent.ExecutionContext


object flipper
{
  var current: Boolean = false
}


@Singleton
class ScalaCometController @Inject()
  (
    implicit actorSystem: ActorSystem,
    ec: ExecutionContext
  )
  extends Controller  {

  //Error handling for streams
  //http://doc.akka.io/docs/akka/2.5.2/scala/stream/stream-error.html
  val decider: Supervision.Decider = {
    case _                      => Supervision.Restart
  }

  implicit val mat = ActorMaterializer(
    ActorMaterializerSettings(actorSystem).withSupervisionStrategy(decider))


  val (sink, source) =
    MergeHub.source[JsValue](perProducerBufferSize = 16)
      .toMat(BroadcastHub.sink(bufferSize = 256))(Keep.both)
      .run()

  def streamClock() = Action {
    Ok.chunked(source via Comet.json("parent.clockChanged")).as(ContentTypes.HTML)
  }

  def kickRandomTime() = Action {

    var finalJsonValue:JsValue=null

    flipper.current = !flipper.current
    if(flipper.current) {
      finalJsonValue = Json.toJson(Location(1.0,1.0))
    } else {
      val rand = DateTimeFormatter.ofPattern("ss mm HH").format(ZonedDateTime.now().minusSeconds(scala.util.Random.nextInt))
      finalJsonValue = Json.toJson(Resident("Hove", 12, Some(rand)))
    }
    Source.single(finalJsonValue).runWith(sink)
    Ok(s"We sent '$finalJsonValue'")
  }

}

Client side Comet frame

The Play Framework has support for web sockets, and we could have used a web socket, but there are times when you dont want to initiate some comms, you just want notifications of something that happened on the server. Luckily the Play Framework has support for this too using Comet, which you read more about here : https://www.playframework.com/documentation/2.5.x/ScalaComet

What this really boils down to is have a IFRAME that is permanently part of the rendered HTML (forever-iframe technique) that is linked up to a comet play url. For me this is like this

 

/scala/comet/liveClock

 

Where it can be seen that this uses the route

 

GET        /scala/comet/liveClock              controllers.ScalaCometController.streamClock()

 

Which we saw above

 

Client side RX comet listener

So we have now seen how the back end produces a stream of differing JSON object (ok toggles between 2 fixed JSON objects), and how we use the forever-iframe idea with a comet based Play route. So what about the message coming into the JavaScript what does that look like?

 

Well here is the relevant code, where we wrap the code we want to run in a self executing function. There are a couple of points here of note

Ok.chunked(source via Comet.json("parent.clockChanged")).as(ContentTypes.HTML))
  • We create a top level window function called clockChanged which ties up with the back end comet route code (
  • We create a custom event that we dispatch via the window
  • We use that custom event to create an Rx Observable (I want this separation as long term the Rx code will become a service that is injected into the props of a React component from the IOC container)

 

import Rx from 'rx';  


(function () {

    var evt;

    window['clockChanged'] = function (incomingJsonPayload) {
        evt = new CustomEvent('onClockChanged', { detail: incomingJsonPayload });
        window.dispatchEvent(evt);
    }

    var source = Rx.Observable.fromEvent(window, 'onClockChanged');

    var subscription = source.subscribe(
        function (x) {
            console.log('RX saw onClockChanged');
            console.log('RX x = ', x.detail);
        },
        function (err) {
            console.log('Error: %s', err);
        },
        function () {
            console.log('Completed');
        });

} ());

And that is all there is to that part. As I say this is demo code and will be moved about a bit over time, but it does show the moving parts ok.

How do integrate our existing webpack based front end with Play

 

Up until now I have avoided talking about the view, and Play and the code from the webpack based front end we have seen in the previous posts. Things is we can do views in play but I was kind of keen to do all my front end work in TypeScript/react/webpack. So I wanted play to merely serve up my front end code from those tools. What that means is really serving up the final JS/CSS bundles and index.html produced by webpack and the awesome HtmlWebpackPlugin (that uses a template and injects the right script bundles). So can this webpack based workflow work ok with Play. I thought about it a bit, and came up with this.

 

Making sure webpack generates bundles to correct place

The first thing to do was to include the code in the Play application at some location. For me this as follows:

image

You want to avoid putting to much stuff under the public folder in Play as Play treats these files as static files, that can be cached etc etc, so 100MB of node modules should defo not go under public if you can help it.

 

Then from there we need to ensure the bundles get built to the correct location, which for my setup and my Play application meant the public folder

 

Making sure bundles are prefixed

The other thing I needed to do was make sure the webpack output was setup to have the correct public path.

To do the 2 things above, I changed my webpack config file to this

image

With these changes in place this is what I now get for my index.html page in the public\dist folder

 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>

       
		<link href="/assets/dist/vendor.bundle.994d68871d44c4fc440c.css" rel="stylesheet">
		<link href="/assets/dist/indexCss.bundle.994d68871d44c4fc440c.css" rel="stylesheet"></head>
    <body>
         
        
<!-- Main --> /assets/dist/vendor.bundle.994d68871d44c4fc440c.js /assets/dist/index.bundle.994d68871d44c4fc440c.js </body> </html>

 

See how the bundles point to the assets folder, which we now know Play will serve using the AssetsController.

 

Serving the initial webpack generated HTML page

The last piece of the puzzle is how do we get a standard play route to serve up this index.html file? Well here is how

 

package controllers


import javax.inject.Inject

import play.api.mvc.{Action, Controller}

class HomeController @Inject() (environment: play.api.Environment)
  extends Controller {

  def index() = Action {
    val fullpath = s"${environment.rootPath}\\public\\dist\\index.html"
    val htmlContents = scala.io.Source.fromFile(fullpath).mkString
    Ok(htmlContents).as("text/html")
  }

}

 

Quite cunning no, we just read the file as text, and serve the string contents as HTML. Where the route for this is still using the standard Play route

 

GET        /                                   controllers.HomeController.index()

 

And bam, we now have a streaming Akka/Play/Webpack app working. Neato

 

A Demo

Now that I have talked about most of the parts, lets see a little demo of it working, so we need to load our site up (which is the GET / route above)

image

Our ugly but functional, react page is shown. Not very exciting, lets see the console (clear it), and load up the great REST tool Postman, and poke the stream simulation endpoint, and watch our Rx JavaScript code write out to the console

 

Here is the console after a few Send clicks in postman for the http://localhost:9000/scala/comet/kick route

image

 

Conclusion

So that is all I wanted to say this time. I think it has worked out fairly well. Next time we will be looking at adding react-routing into the fold, and create some routes, and dummy place holder pages which we develop along the way (after we do the post on prototyping the screens of course)

MADCAP IDEA PART 2 : ADDING DI/IOC TO THE CLIENT SIDE FRONT END WEB SITE

 

Last Time

Last time we built a bare bones react/webpack/babel/typescript/bootstrap web site. You can read that post here should you wish to: https://sachabarbs.wordpress.com/2017/05/15/madcap-idea-part-1-start-of-the-client-side-portion-of-the-web-site/

 

PreAmble

This post will be about adding DI/IOC to the bear bones no thrills client portion of the web site that we built last time. Just as a reminder this is part of my ongoing set of posts which I talk about here :

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

What Is DI/IOC?

In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

This fundamental requirement means that using values (services) produced within the class from new or static methods is prohibited. The class should accept values passed in from outside.

The intent behind dependency injection is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one.

Dependency injection is one form of the broader technique of inversion of control. Rather than low level code calling up to high level code, high level code can receive lower level code that it can call down to. This inverts the typical control pattern seen in procedural programming.

As with other forms of inversion of control, dependency injection supports the dependency inversion principle. The client delegates the responsibility of providing its dependencies to external code (the injector). The client is not allowed to call the injector code. It is the injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code. The client does not need to know how to construct the services. The client does not need to know which actual services it is using. The client only needs to know about the intrinsic interfaces of the services because these define how the client may use the services. This separates the responsibilities of use and construction.

There are three common means for a client to accept a dependency injection: setter-, interface- and constructor-based injection. Setter and constructor injection differ mainly by when they can be used. Interface injection differs in that the dependency is given a chance to control its own injection. All require that separate construction code (the injector) take responsibility for introducing a client and its dependencies to each other

 

From https://en.wikipedia.org/wiki/Dependency_injection

 

 

What Choices Do We To Do This When Working With React?

There are several choices we have when working with React, such as

  • Using react context
  • Using the module system
  • Using a 3rd party DI/IOC system (I will be covering this, in this post)

 

There is VERY good post on these different techniques here : https://github.com/krasimir/react-in-patterns/tree/master/patterns/dependency-injection it is a great read and I suggest you take a look to gain a better understanding of some of the more obscure areas of react (context I am looking at you)

 

 

Which 3rd Party DI/IOC Library Did I Chose And Why?

I have decided to use the https://github.com/inversify/InversifyJS (Inversify JS) DI/IOC library. Why did I chose this one, well there were quite a few reasons:

 

  • It is written in TypeScript, and I wanted to use TypeScript where possible
  • Is looks like a good full featured container, that reminds me of many other containers that I have worked with in .NET/Scala
  • It is quite mature and is in version 2.0
  • It has quite a bit of good press around it
  • The documentation is good
  • It did what I wanted

 

So those were my reasons, so what do we have to do to get Inversify JS to work?

 

Installation

 

Node Module Installation

The first steps is to make sure we have the correct node packages, to do this we just need to issue the following  NPM (node package manager) command line :

npm install inversify reflect-metadata --save

 

Once this is done you should have the following entries in your package.json file

 

image

 

 

Changes To The TypeScript “tsconfig.json” File

The other thing that Inversify JS needs is a couple of specific TypeScript settings. These need to go in the tsconfig.json file, the new lines since last time are these

 

image

 

 

 

 

That is all the setup we need, so let’s now have a look at using the Inversify JS DI/IOC classes, decorators etc etc

 

 

Creating Some Injectable Thing

Lets start with creating something that can be injected with other values, and can be resolved from the container.

 

In Inversify JS  a type has to be marked as @Injectable, which is not something you have to do in other DI/IOC offerings. This is more than likely a requirement because JavaScript is a dynamic language and these decorators are used to create extra metadata to ease in the resolution mechanisms used by ALL IOC containers.

 

To actual get something injected into a class we need to use the @Inject decorater.

 

Here is an example of a class that can be resolved from the Inversify JS  IOC container, and will also have it constructor dependencies resolves by the Inversify JS  IOC container.

 

import { injectable, inject } from "inversify";
import { TYPES } from "../types";

@injectable()
export class Foo {

    private _num: number;

    constructor(@inject(TYPES.SomeNumber) num: number) {
        this._num = num;
    }

    getNum() {
        return this._num * 2;
    }

}

 

You can see that we also use some TYPES. What are these, lets take a look at that.

export const TYPES = {
    Foo: Symbol("Foo"),
    SomeNumber: Symbol("SomeNumber")
};

 

It can be seen this TYPES constant just offers us a way of using Symbol as runtime identifiers for our dependencies.

 

Ok so now that we have a class that expects to have its dependencies satisfied from the Inversify JS IOC Container, and is itself resolvable, lets see how we can configure the container.

 

Creating The Container

I have chosen to create a singleton object for my container, which looks like this

import "reflect-metadata";
import { Container } from "inversify";
import { TYPES } from "../types";
import { Foo } from "../domain/Foo";

export class ContainerOperations {
    private static instance: ContainerOperations;
    private _container:Container = new Container();

    private constructor() {
        
    }

    static getInstance() {
        if (!ContainerOperations.instance) {
            ContainerOperations.instance = new ContainerOperations();
            ContainerOperations.instance.createInversifyContainer();
        }
        return ContainerOperations.instance;
    }

    private createInversifyContainer() {
        this.container.bind<number>(TYPES.SomeNumber).toConstantValue(22);
        this.container.bind<Foo>(TYPES.Foo).to(Foo);
    }

    public get container(): Container {
        return this._container;
    }
}

 

There are a couple of things to note in the above code:

  • We import “reflect-metadata”, “Container” and “TYPES”
  • We have our singleton that simple wraps the Inversify JS IOC container.
  • We configure the the container registrations (bindings in Inversify JS speak)

 

And that is all there is to that part. So all that is left, is to actually resolve something from the container. We will look at that next

 

Resolving Something From The Container

 

As I am using react, I will likely be using the Inversify JS IOC container to assist me with creating the props for the react components. That is not strictly relevant to this discussion, so lets just see how we can resolve an instance of the Foo IOC registered class using Inversify JS

 

We do this as follows:

import { Foo } from "./domain/Foo";
import { TYPES } from "./types";
import { ContainerOperations } from "./ioc/ContainerOperations"; 

let foo = ContainerOperations.getInstance().container.get<Foo>(TYPES.Foo);

 

It can be seen that we simple make use of the singleton (that wraps the container) to resolve our Foo class. Happy days

 

Conclusion

I have to say I did struggle a bit with getting Inversify JS up and running in my project. But I also have to say that I asked a question on the Inversify forum and the author Remo Jansen was absolutely brilliant in helping me to get my stuff to run. To the point where I pointed him at my GitHub repo, and he looked at, got it to work, and sent me a pull request.

 

Remo I tip my hat to you sir, top library, top fella. And as promised I owe you that beer

 

So once it was installed, I found it very easy to work with, it soon felt like many other IOC frameworks I have used (NInject, Funq,Munq, Castle, AutoFac, Unity, MEF take your pick). I was very happy with the results.

 

As I previously stated I will be continuing to write posts which will be tracked on Trello : https://trello.com/b/F4ykCOOM/kafka-play-akka-react-webpack-tasks

MadCap Idea part 1 : Start of the Client side portion of the web site

 

PreAmble

This post will be about building the bear bones no thrills client portion of the web site that is part of my ongoing (well this is the first, so ongoing after this) set of posts which I talk about here :

 

https://sachabarbs.wordpress.com/2017/05/01/madcap-idea/, where we will be building up to a point where we have a full app using lots of different stuff, such as these

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

 

 

Introduction

So let me just apologize for how long this one has taken to put together, I never envisaged that this post would take me quite as long as it has. That said it has only taken 5-6 days where I have spent a maximum of 2 hours on it, and when I started this post I had a VERY rough idea of how webpack worked and what it did, but I had NEVER tried to create a webpack project from scratch, so not so bad in the end, I am fairly happy with the results.

 

Where is the code?

If you prefer to just have a look at the end result you can see that here : https://github.com/sachabarber/MadCapIdea

 

What did I want to get to work VS what is working?

Before I started this post/code I had a set list of requirements in mind, which I will show in the table below. I will also show whether I managed to get that feature to work or not

Feature Does It Work
I wanted to use Web pack to manage the build Yes
I wanted to use Typescript via Babel to regular JavaScript Yes
I wanted 3rd party libraries to be used with their typing information No
I wanted to be able to bundle my eventually transpiled JavaScript code into a single bundle Yes
I wanted to be able to use SCSS/SASS for my CSS needs and have them transpiled to CSS Yes
I wanted to be able to bundle my eventually transpiled CSS code into a single bundle Yes
I wanted to be able to import/export stuff (JS /CSS / Files etc etc) using ES6 modules Yes
I wanted to be able to trace back from minified JavaScript bundle back to my original TypeScript via SourceMaps Yes
I wanted to be able to use a fully SourceMap enabled DEVELOP version of my webpack setup Yes
I wanted to be able to run a streamlined (minification, no comments, no console.log, no SourceMap) PRODUCTION version of my webpack setup Yes
I wanted to be able to use React Yes
I wanted to be able to use Bootstrap-React Yes
I wanted to have the option to use JQuery/Lodash as I would in a simple standard JavaScript project, ie as “$” and “_” respectively Yes

 

As you can see I did actually manage to get ALL of this to work with the one exception of the typings for 3rd party libraries. The code still works at runtime, but there is just something hinky going on outside of runtime.

 

<rant>

I would just like to spend a moment ranting about just how much disinformation is out there on the whole module/typescript/webpack space. I must have read about 100 posts, all with different setups, all with different tsconfig.json files, all suggesting different webpack setups. On one hand my god weback/TypeScript are cool, but you have to be VERY careful what you apply. If you get your tsconfig.json into an invalid state, you just may find that editing TypeScript no longer works inside Visual Studio.

 

I have lost track of just how many different approaches I took to try and resolve the unknown module issue in TSX (TypeScript JSX react files). Even the official walk through on the TypeScript.org web site doesn’t work for me. Things I tried and failed at were

  • Including typings in my tsconfig.json
  • Include reference headers in my TypeScript files
  • Including a reference.ts file for 3rd party typings
  • Messing about with moving types\xxxxFramework into full blown NPM Dependencies rather than NPM DevDependencies
  • Various module settings inside of tsconfig.json

All failed, so if anyone out there that is a TypeScript / React / WebPack guru, please let me know what I am doing wrong.  The funny thing is that EVERYTHING is 100% fine at runtime.

</rant>

 

Ah that feels better, anyway now that, that is out of my system, lets continue shall we….

 

 

Webpack fundamentals

So what exactly is webpack. I think this image from the webpack web site https://webpack.js.org/ does a fairly good job of described at a glance what webpack is all about

 

image

 

So clear? No? Ok lets try some words as well

 

  • Webpack at its heart is a bundler which is able to offer module support, and is able to bundle a lot of different things into bundles
  • Webpack is able to bundle lots of thing via a technique called “loaders”, loaders can be piped one to the next (just like a command line)
  • Webpack also offers module support for
    • AMD
    • CommonJS
    • ES6 modules
  • Webpack comes with SourceMap support (one of my favorite things ever, maybe even better than ice cream, but no where near as good as BBQ food and beers)
  • Webpack comes with inbuilt minification support (thanks to Uglify.js : https://www.npmjs.com/package/uglifyjs)
  • Webpack works seamlessly with NPM (Node package manager)
  • Webpack is able to watch your files and produce new packages based on the diff of what you edited compared to what was previously built
  • Webpack supports the idea of base/different configs such that you may have different environment configs DEV|PROD (typically you want loads of debugging aids in dev)

 

So in a nutshell that is what webpack is all about. We will dive into some of the sub areas in a bit more details below before we examine the actual use cases that I set out to solve

 

This may all seem a bit overwhelming, but with webpack it mainly boils down to a config file (typically called webpack.config.js). Here is a minimal example

const { resolve } = require('path');

const webpack = require('webpack');

// plugins
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = (env) => {

  return {
    context: resolve('src'),
    entry: {
      app: './main.ts'
    },
    output: {
      filename: '[name].[hash].js',
      path: resolve('dist'),
      // Include comments with information about the modules.
      pathinfo: true,
    },

    resolve: {
        extensions: [
            '',
            '.js',
            '.ts',
            '.tsx'
        ]
    },

    devtool: 'cheap-module-source-map',

    module: {
      loaders: [
        { test: /\.tsx?$/, loaders: [ 'awesome-typescript-loader' ], exclude: /node_modules/ }
      ],
    },

    plugins: [

      new HtmlWebpackPlugin({
        template: resolve('src','index.html')
      })

    ]

  }
};

 

We will be diving into this, and a lot more within this post.

 

Node

As stated Node/NPM is a fairly vital part of working with webpack, so you will need to ensure you have done the following as a minimum

  • Installed node
  • Installed NPM
  • Installed webpack globally : npm install webpack –g

 

Most of the stuff I talk about in this post requires installing via NPM. But You have a copy of all the requirements inside the package.json file. Which at the time of writing this post looked like this

 

{
  "name": "task1webpackconfig",
  "version": "1.0.0",
  "description": "webpack 2 + TypeScript 2 + Babel example",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/sachabarber/MadCapIdea.git"
  },
  "keywords": [
    "babel",
    "typescript",
    "webpack",
    "bundling",
    "javascript",
    "npm"
  ],
  "author": "sacha barber",
  "homepage": "https://github.com/sachabarber/MadCapIdea#readme",
  "dependencies": {
    "bootstrap": "^3.3.7",
    "jquery": "^3.2.1",
    "lodash": "^4.17.4",
    "react": "^15.5.4",
    "react-bootstrap": "^0.31.0",
    "react-dom": "^15.5.4",
    "webpack": "^2.5.0",
    "webpack-merge": "^4.1.0"
  },
  "devDependencies": {
    "@types/jquery": "^2.0.43",
    "@types/lodash": "^4.14.63",
    "@types/react": "^15.0.24",
    "@types/react-dom": "^15.5.0",
    "awesome-typescript-loader": "^3.1.3",
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2015-native-modules": "^6.9.4",
    "babel-preset-react": "^6.24.1",
    "css-loader": "^0.28.1",
    "extract-text-webpack-plugin": "^2.1.0",
    "html-webpack-plugin": "^2.28.0",
    "node-sass": "^4.5.2",
    "on-build-webpack": "^0.1.0",
    "sass-loader": "^6.0.3",
    "source-map-loader": "^0.2.1",
    "typescript": "^2.3.2",
    "webpack": "^2.4.1"
  },
  "scripts": {
    "build-dev": "webpack -d --config webpack.develop.js",
    "build-prod": "webpack --config webpack.production.js"
  }
}

 

 

 

Loaders

Loaders are probably the MOST important webpack concept to learn. There is practically a loader for EVERYTHING. But what exactly is a loader?

Well quite simply, a loader is a way to take some source file contents, and bundle it up in the final artifact. However things can get more sophisticated as some loaders are also able to transpile (act of converting code written in one language into another language say TypeScript –> JavaScript, or ES6 JavaScript –> ES5 JavaScript).

 

Loaders may also be piped together where the loaders declared run from right most to left most (or bottom to top, if you have them over multiple lines). This is EXTREMELY powerful, as it enables this sort of workflow in the demo code

  • Write code in TypeScript (using good stuff like classes (ok ES6 has those but you get me), interfaces, async-await etc etc)
  • Have that run through Babel.Js (bring future JS functions to you by converting your future JS into JS that runs in browsers now)
  • Finally into plain old JS that is compatible with today’s browsers (they will all catch up one day, actually they won’t so yeah babel.js is here to help)

Loaders are not just for JS, they can be used for CSS/Images/Fonts all sorts of things

 

We will see examples on this stuff when we get into the guts of things

Code dissection

In this section we will dissect the code contained at the github repo, and talk through all my initial requirements and see how they ended up being implemented

 

Bundles

One of the main reason to want to use webpack is for its bundling abilities, where I wanted to be able to bundle the following things

  • Typescript which is transpiled to JavaScript (thanks to a TypeScript loader)
  • SCSS/SASS/Css (thanks to a Sass loader)
  • Images(thanks to a Url loader)

So that is what we are trying to bundle, but there are a few things that need to be done to make that happen, so lets start with the loaders (I will be covering images and fonts later, so for now lets just talk about JavaScript and CSS bundling)

 

JavaScript Bundling

As I say I wanted the option to use TypeScript or regular JavaScript, and I also wanted to be able to use SASS or regular CSS so we start with these loaders which will traverse the source code and find all the relevant files (see the little regex that’s used to find the files) and will then bundle these files

 

let _ = require('lodash');
let webpack = require('webpack');
let path = require('path');
let fs = require("fs");
let WebpackOnBuildPlugin = require('on-build-webpack');
let ExtractTextPlugin = require('extract-text-webpack-plugin');
let HtmlWebpackPlugin = require('html-webpack-plugin');

let babelOptions = {
    "presets": ["es2015", "react"]
};

function isVendor(module) {
    return module.context && module.context.indexOf('node_modules') !== -1;
}

let entries = {
    index: './src/index.tsx'

};

let buildDir = path.resolve(__dirname, 'dist');

module.exports = {

    context: __dirname,

    entry: entries,

    output: {
        filename: '[name].bundle.[hash].js',
        path: buildDir
    },

    
    
    resolve: {
        extensions: [".tsx", ".ts", ".js", ".jsx"],
        modules: [path.resolve(__dirname, "src"), "node_modules"]
    },

    plugins: [

       

        // creates a common vendor js file for libraries in node_modules
        new webpack.optimize.CommonsChunkPlugin({
            names: ['vendor'],
            minChunks: function (module, count) {
                return isVendor(module);
            }
        }),

        // creates a common vendor js file for libraries in node_modules
        new webpack.optimize.CommonsChunkPlugin({
            name: "commons",
            chunks: _.keys(entries),
            minChunks: function (module, count) {
                return !isVendor(module) && count > 1;
            }
        }),


        //scss/sass files extracted to common css bundle
        new ExtractTextPlugin({
            filename: '[name].bundle.css',
            allChunks: true,
        }),

        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'template.html',
        })
    ],

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader' 1st 
            // then 'babel-loader'
            // NOTE : loaders run right to left (think of them as a cmd line pipe)
            {
                test: /\.ts(x?)$/,
                exclude: /node_modules/,
                use: [
                  {
                      loader: 'babel-loader',
                      options: babelOptions
                  },
                  {
                      loader: 'awesome-typescript-loader'
                  }
                ]
            },


            // All files with a .css extenson will be handled by 'css-loader'
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract(['css-loader?importLoaders=1']),
            },

            // All files with a .scss|.sass extenson will be handled by 'sass-loader'
            {
                test: /\.(sass|scss)$/,
                loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
            },


            // All files with a '.js' extension will be handled by 'babel-loader'.
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                  {
                      loader: 'babel-loader',
                      options: babelOptions
                  }
                ]
            },


            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            {
                enforce: "pre",
                test: /\.js$/,
                loader: "source-map-loader"
            }
        ]
    }
};

 

 

The bulk of the code above is made up of loaders. But there are a few things above that deserve special call outs, namely

 

Resolve

This tells us what type of files webpack should try and resolve

 

resolve: {
	extensions: [".tsx", ".ts", ".js", ".jsx"],
	modules: [path.resolve(__dirname, "src"), "node_modules"]
},

 

Entry

These are the main entry points into the code. So for me this is the index.tsx file.

 

let entries = {
    index: './src/index.tsx'

};


entry: entries,

 

 

Output

This is where you tell webpack what the name of your final bundles will be, which will contain all the code files that matches the regex test that was setup in the loaders. It is VERY important to note that ALL the files that matches the loader regex will become part of the bundle file.

 

output: {
	filename: '[name].bundle.[hash].js',
	path: buildDir
},

 

 

TypeScript

I wanted the option to be able to use TypeScript IF I WANTED to. So to do this we need a webpack loader, there are a couple  of TypeScript loaders for webpack. But I went with awesome-typescript-loader. I also want to run my TypeScript files through Babel. We will get onto what Babel brings to the party in just a second, but for now just understand that TypeScript and Babel act as transpilers where they take JavaScript using features that is not available in regular JavaScript and transpile that code into regular JavaScript that today’s browsers understand. Obviously since the final product of both TypeScript and Babel is regular JavaScript we also need a loader for that too.

 

Here is my TypeScript/Babel/JavaScript setup.

// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader' 1st 
// then 'babel-loader'
// NOTE : loaders run right to left (think of them as a cmd line pipe)
{
	test: /\.ts(x?)$/,
	exclude: /node_modules/,
	use: [
	  {
		  loader: 'babel-loader',
		  options: babelOptions
	  },
	  {
		  loader: 'awesome-typescript-loader'
	  }
	]
},

// All files with a '.js' extension will be handled by 'babel-loader'.
{
	test: /\.js$/,
	exclude: /node_modules/,
	use: [
	  {
		  loader: 'babel-loader',
		  options: babelOptions
	  }
	]
}

 

The other thing you need when working with TypeScript is a tsconfig.json file. Here is mine, you can see that I have configured mine to be react friendly.

 

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": false,
    "module": "es2015",
    "target": "es5",
    "jsx": "react",
    "types" : ["jquery", "lodash", "react", "react-dom"]
  },
    "include": [
        "./src/**/*"
    ]
}

 

NOTE: You need to be a bit careful with this file, if you mess it up, you may find yourself in a quite sad position where you can no longer edit TypeScript files in Visual Studio.

 

Babel

I just showed you the babel loader, so I won’t repeat that. But just what is this Babel you speak of. Well here the blurb from the Babel.js website

 

Babel has support for the latest version of JavaScript through syntax transformers. These plugins allow you to use new syntax, right now without waiting for browser support.

 

This is the sort of stuff that Babel allows you to write right now.

 

image

 

The only other thing you need for Babel is to give a little config file called .babelrc which for me just contains this

 

{ "presets": ["es2015","react"] }

 

And that is pretty much all there is to it, you can now use these features in your JavaScript. Neato

 

SCSS

I don’t mind CSS, but these days there are better tools out there, namely LESS/SASS. What these tools offer you are things like this

  • Modular CSS (multiple files in a heirachy)
  • Nested CSS rules
  • Variables
  • etc etc

 

So it seems strange NOT to want to work with this. As with most things in webpack, it starts with a loader, where we have support for SASS and also plain CSS. Remember loaders run from right to left, so in the case of the SASS/SCSS file match, the files will 1st run through the sass-loader the the css-loader. However for plain old CSS they just go through the css-loader

 

// All files with a .css extenson will be handled by 'css-loader'
{
	test: /\.css$/,
	loader: ExtractTextPlugin.extract(['css-loader?importLoaders=1']),
},

// All files with a .scss|.sass extenson will be handled by 'sass-loader'
{
	test: /\.(sass|scss)$/,
	loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
},

 

The other part of the puzzle to get CSS to work is this ExtractTextPlugin that you can see mentioned in the loader sections just above. What the ExtractTextPlugin  does is to extract all the text from the individual CSS files (yep that’s right SASS/SCSS is transpiled to regular CSS) into a single CSS file.

 

//scss/sass files extracted to common css bundle
new ExtractTextPlugin({
    filename: '[name].bundle.[hash].css',
    allChunks: true,
}),

 

Don’t be too scared by the [name] and [hash] stuff just yet we will get onto to that later.

 

 

Bootstrap

So for those of you living under a rock there is a great library (started by Twitter engineers) to help create responsive uniform looking sites. This library is called twitter Bootstrap. It comes with various components and CSS, and use typeography for its icons.

 

Now Bootstrap is great, but I wanted to use React, and React has the concept of a virtual DOM, and generally speaking tries to work with it own Virtual DOM rather than the real DOM. This has led to a specialized version of Bootstrap specifically for use with React. Naturally I needed to get that to work. It is called React-Bootstrap.

 

So once we have it installed via NPM we just need to worry about a few small thing

 

Images

These are loaded by (surprise surprise) another bootstrap loader section

{ 
	test: /\.png$/, 
	loader: "url-loader?limit=100000" 
},

{ 
	test: /\.jpg$/, 
	loader: "file-loader" 
},

{
	test: /\.svg(\?.*)?$/,
	loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=image/svg+xml'
},

 

Fonts

Fonts are also loaded by more webpack loaders

{
	test: /\.woff(\?.*)?$/,
	loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/font-woff'
},

{
	test: /\.woff2(\?.*)?$/,
	loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/font-woff2'
},

{
	test: /\.ttf(\?.*)?$/,
	loader: 'url-loader?prefix=fonts/&name=fonts/[name].[ext]&limit=10000&mimetype=application/octet-stream'
},

{
	test: /\.eot(\?.*)?$/, loader: 'file-loader?prefix=fonts/&name=fonts/[name].[ext]'
},

 

 

Css

So once you have all the other stuff done you can proceed to just use react-bootstrap. Here is a small example from one of my TypeScript files

 

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Button } from 'react-bootstrap';

import 'bootstrap/dist/css/bootstrap.css';

export class Hello extends React.Component<HelloProps, undefined> {
    render() {
        return 
<div>
                <Button bsStyle="primary" bsSize="large">Large button</Button>
                
<h1 id="helloText">Hello from {this.props.compiler} and {this.props.framework}!</h1>

               </div>

;
    }
}

 

Which when rendered looks like this:

image

 

 

 

Lodash

Lodash is a kind of new underscore-esque library, which offers many convenience methods on collections. It is like the LINQ to obejcts of the JavaScript world. To work with Lodash you can simply import it as follows

 

import * as _ from "lodash";

 

Which we could verify quite simply with something like this, where the image below is me finding the original line in my TypeScript file within the SourceMap that was sent to the browser and putting a break point on the line I wanted to debug

 

console.log(_.VERSION);

image

 

 

JQuery

Ah the blessed Jquery, love it or hate it, there is certainly a lot of it on the web. And at times it is still very convenient, so we should really allow for it too. Thing with JQuery is that it wants to be available as global variable $ or via a property on window. Is this even possible with webpack? Well yes it is, we simply add the following bit of config within the webpack Plugins section

 

//The ProvidePlugin makes a module available as a variable in every other
//module required by webpack
new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    "window.jQuery": "jquery"
}),

 

That then allows us to to use Jquery like this without having to ever import it anywhere, its just automatically globally available

 

console.log("jquery");
console.log($);
console.log($.fn.jquery);

 

Again I am using the emitted SourceMap to find my original TypeScript code

 

image

 

 

Source Map Support

I also wanted to be able to debug my ORIGINAL TypeScript/JavaScript, so using SourceMaps WAS A MUST. By using source maps in webpack I am able to send the transpiled/bundled (but not minified I only do that in production mode), and also view the original code, and set break points in the original code.

 

This is the JavaScript bundle that webpack sent

 

image

 

And here is me inside the SourceMap file, see how I am in the original content here (ie the code I wrote)

 

image

 

 

This is enabled via the webpack setting

devtool: "source-map"

 

ES6 style code and modules

Another feature of using webpack is that you may using AMD/CommonJS modules (or if you included TypeScript/Babel ES6 modules). I am using TypeScript and Babel so I went with ES6 style modules, which means I can export/import things like this:

import * as React from "react";
import * as ReactDOM from "react-dom";
import * as _ from "lodash";
import { Button } from 'react-bootstrap';

import 'bootstrap/dist/css/bootstrap.css';

export interface HelloProps { compiler: string; framework: string; }

export class Foo {

    private _num: number;

    constructor(num: number) {
        this._num = num;
    }

    getNum() {
        return this._num * 2;
    }

}

 

 

Html Plugin

Ok hope you all recall but a while ago I promised to explain what was meant by [name] and [hash] in my webpack config.

  • [name] : simply gets replaced by the current bundle name
  • [hash] : produces a hash of the bundle

I think name is self explanatory, but [hash] is an interesting one.  The idea of producing a hash for your bundles is great. That means if the file contents change the hash produced is different, so the browser cache would be invalidated.

 

That’s cool. But hang on how do we normally include script/css references in our Html page, either in Script/head tags right? And if the hash is changing all the time, how can we possible link to files where we don’t know what the hash will be.

 

Luckily we just use the HtmlWebpackPlugin, which does a great job of taking a template for the original HTML we want to end up with, and putting the final bundle generated references into a copy of the template and copying that final HTML file to the desired output directory.

 

So for me I have this webpack config

new HtmlWebpackPlugin({
    filename: 'index.html',
    template: 'template.html',
})

 

Where my template.html file looks like this

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        
<div id="example"></div>

        <!-- Main -->
    </body>
</html>

 

And once webpack / HtmlWebpackPlugin have run their magic, the resultant HTML (ie final HTML file) looks like this:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
		<link href="vendor.bundle.b8e27b8c09179b83b9b1.css" rel="stylesheet">
		<link href="indexCss.bundle.b8e27b8c09179b83b9b1.css" rel="stylesheet"></head>
    <body>
        
<div id="example"></div>

        <!-- Main -->
		<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%20src%3D%22vendor.bundle.b8e27b8c09179b83b9b1.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
		<script type="text/javascript" src="index.bundle.b8e27b8c09179b83b9b1.js">
	</body>
</html>

 

See how it just inserts the CSS/JS bundles for me, and my hashing for the bundles now seemlessly happens and I don’t have to worry about it ever again

 

Separate Configs

The final thing I wanted to cover was how to have different DEV/PROD webpack configs. Up until now I have just been showing you a base config file. But we can use webpack-merge to allow us to create bespoke webpack config files for specific environments.

For example here is my Develop webpack file (which is the same as the base config file)

 

let commonConfig = require('./webpack.config.js');
let webpack = require('webpack');
let Merge = require('webpack-merge');

module.exports = function (env) {
    return Merge(commonConfig, {})
}

 

Whilst this is my Production webpack config file where I want

  • No SourceMap files
  • No console.log
  • No comments
  • Minification

 

let commonConfig = require('./webpack.config.js');
let webpack = require('webpack');
let Merge = require('webpack-merge');

module.exports = function (env) {
    return Merge(commonConfig, {
        plugins: [
          new webpack.LoaderOptionsPlugin({
              minimize: true,
              debug: false
          }),
          new webpack.optimize.UglifyJsPlugin({
              // Eliminate comments
              comments: false,
              beautify: false,
              mangle: {
                  screw_ie8: true,
                  keep_fnames: true
              },
              compress: {
                  screw_ie8: true,

                  // remove warnings
                  warnings: false,

                  // Drop console statements
                  drop_console: true
              },
              comments: false,
              sourceMap: false
          })
        ]
    })
}

 

 

 

Conclusion

So that is all I wanted to say this time, as I stated in the 1st post I will be continuing to write posts which will be tracked on Trello : https://trello.com/b/F4ykCOOM/kafka-play-akka-react-webpack-tasks

MADCAP IDEA

INTRODUCTION

So this is somewhat of strange post, or should I say what will hopefully become a decent set of posts, thing is, I have no idea how this will end up really,
as I have not embarked on a mission like this before. So please bear with me.

SO JUST WHAT IS IT THAT I AM TALKING ABOUT?

Well the way I typically like to run my blog/code project articles / life, is that I pick a technology and
just concentrate on it for a while and write about it. This time however I have decided to treat my blogging/articles as a bit more
of a work like escapade, where I will be assigning mini tasks (think JIRA tickets) to myself, some of which I know nothing about, that should/could in reality be treated
as “spikes” and end up in complete dead ends. It is about the journey after all.

I WILL have a complete list of “tickets” (AKA tasks), which may or may not be completely fleshed out in advance. I will stick to “DOING” those “tickets”
and there is an end goal in sight, and I will outline that in a top level story. I cannot however commit to any timelines, this is as much my journey as it is yours (in fact I mainly
do this stuff for myself, and would highly reccomend it as a way of self improvement). That said I hope people get something out of the series of posts that WILL UNDOUBTEDLY
come from this idea.

You can think of the tasks as “technical tasks” which make up the high level “stories” (in JIRA speak).

This may come across a bit weird, but the technogies I plan to cover in the final product is pretty much a full app, so it’s a little hard to describe in
one blog post/article. So I am hoping that by breaking it down into small chunks, each story/sub task will be a useful learning experience in
it’s own right.

SOURCE CONTROL : ORGANISATION

The idea is that each story/sub task will be a folder/subfolder which is completely independent of other stories/sub tasks (up until the final goal, which is of course
a working showcase that demostrates it all working together).

NOTE TO SELF : I am going to try really hard to do this (aren’t we sacha), as I think one topic -> one source control repo (more than likely GIT), is a good way to
correlate ideas/words on the post/article

 

WHAT DO I WANT TO WRITE


In essence I want to write a very (pardon the pun) but uber simple “uber” type app. Where there are the following funtional requirements

  • There should be a web interface that a client can use. Clients may be a “driver” or a “pickup client” requireing a delivery
  • There should be a web interface that a “pickup client” can use, that shows a “pickup client” location on a map, which the “pickup client” choses.
    The “pickup client” may request a pickup job, in which case “drivers” that are in the area bid for a job.
    The “pickup client” location should be visible to a “driver” on a map
  • A “driver” may bid for a “pickup client” job, and the bidding “driver(s)” location should be visible to the “pickup client”.
  • The acceptance of the bidding “driver” is down to the “pickup client”
  • Once a “pickup client” accepts a “driver” ONLY the assigned “driver(s)” current map position will be shown to the “pickup client”
  • When a “pickup client” is happy that they have been picked up by a “driver”, the “pickup client” may rate the driver from 1-10, and the “driver” may also rate the “pickup client” from 1-10.
  • The rating should only be available once a “pickup client” has marked a job as “completed”
  • A “driver” or a “pickup client” should ALWAYS be able to view their previous ratings. 

Whilst this may sound child’s play to a lot of you (me included if I stuck to using simply CRUD operations), I just want to point out that this app is meant as a learning experience so I will not be using a simple SignalR Hub, and a couple of database tables.

I intend to write this project using a completely different set of technologies from the norm. Some of the technology choices could easily scale to hundreds of thousands of requests per second (Kafka has your back here)

POTENTIAL TECNHOLOGIES INVOLVED

  • WebPack
  • React.js
  • React Router
  • TypeScript
  • Babel.js
  • Akka
  • Scala
  • Play (Scala Http Stack)
  • MySql
  • SBT
  • Kafka
  • Kafka Streams

Some of this will undoubtedly be covered in other blogs (such as React/Webpack), however some of it I am hoping will be quite novel/insightful material.

Who knows though there may be some of you out there that haven’t heard of Webpack, so some of that may even be new, we shall se, hopefully enough stuff for everyone.

STORIES

I will maintain a list of stories and their sub tasks using Trello here : https://trello.com/b/F4ykCOOM/kafka-play-akka-react-webpack-tasks which at the time of writing this post was the items shown below

 

TOP LEVEL STORIES

Web Site

Play Back End

  • Create a back end play app
  • Create test Kafka consumer that is able to read from JSON payload from a Kafka topic
  • Create test publisher that publishes JSON payload to a Kafka topic
  • Create Akka Publisher flow to test EventSource JS call
  • Create login/register API
  • Create check ranking API, which will use Kafka Active queries over KTable (or Global KTable) in the materialized streams
  • Create publish job API, which will publish out on Kafka publisher where it will send a JSON payload
  • Create receive job update API, will read JSON from Kafka Consumer where it will read in JSON payload, with the intention of updating the map of the drivers position
  • Create “Accept Job” API which will publish out on Kafka publisher where it will send JSON payload
  • Create “Bid for Job” API which will publish out on Kafka publisher where it will send JSON payload
  • Create Complete job API, which will publish out on Kafka publisher where it will send a JSON payload
  • Create ranking API, which will publish out on Kafka publisher where it will send a JSON payload
  • Create publish driver job co-ordinate update API, which will publish out on Kafka publisher where it will send a JSON payload

Kafka Streams

Create test app that tests out listening to any single Kafka publisher JSON topic, and creates streams app from it, and pushes out to an output topic

  • Create a windowed Kafka stream app that will window over all “driver bidding” jobs for a give period, and will output to an output stream, such that all the job bids can be consumed by Kafka Consumer
  • Create a paired stream of accepted job (id, client, driver id) and an updated driver position which will come in on a different stream
  • Create a ranking streams app which will store a successful ranking in a Kafka Stream KTable
  • Create a way to use Active Queries for allowing clients/drivers to query their rankings

 

 

HOW WILL PROGRESS BE TRACKED

I will simply use Trellos “Label” facility, such that done tasks will be “Green”, and there will obviously be a post/GitHub code repo folder that goes with that.

 

CAVEATS

1. I will not be concerned with connection failures, the aim of the project is to try and create a real world like project, but not actually create a end-end production grade application
2. I will be treating every run as if it were the first, I will not be storing ANY permanent state (apart from ratings potentially)
3. I will be doing things at my own pace (I have 2 kids) so it comes when it comes

4. I will try and use varied technology choices, which will in places mean that there could potentially be more work required to make it production quality

 

 

 

Crossbar.io quick look at it

A while ago someone posted another SignalR article on www.codeproject.com, and I stated hey you should take a look at this stuff : crossbar.io, and not liking to me someone that is not willing to die by the sword (afting living by it of course) I decided to put my money where my mouth was/is, and write a small article on crossbar.io.

So what its crossbar.io? Well quite simply it is a message broker that has many language bindings, that should all be able to communicate together seamlessly.

Here is what the people being crossbar.io have to say about their own product

Crossbar.io is an open source networking platform for distributed and microservice applications. It implements the open Web Application Messaging Protocol (WAMP), is feature rich, scalable, robust and secure. Let Crossbar.io take care of the hard parts of messaging so you can focus on your app’s features.

I decided to take a look at this a bit more which I have written up here : https://www.codeproject.com/Articles/1183744/Crossbar-io-a-quick-look-at-it

A Look At Docker

A while ago I worked on a project that used this tech stack

  • Akka HTTP : (actually we used Spray.IO but it is practically the same thing for the purpose of this article). For those that don’t know what Akka HTTP is, it is a simple Akka based framework that is also able to expose a REST interface to communicate with the actor system
  • Cassandra database : Apache Cassandra is a free and open-source distributed database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. Cassandra offers robust support for clusters spanning multiple datacenters, with asynchronous masterless replication allowing low latency operations for all clients.

It is a multi node cluster

This was a pain to test, and we were always stepping on each others toes, as you can imagine running up a 5 node cluster of VMs just to satisfy my each developers own testing needs was a bit much. So we ended up with some dedicated test environments, running 5 Cassandra nodes. These was still a PITA to be honest.

This got me thinking perhaps I could use Docker to help me out here, perhaps I could run Cassandra in a Docker container, hell perhaps I could even run my own code that uses Cassandra in a Docker container, and just point my UI at the Akka HTTP REST server running in Docker. mmmmm

I started to dig around, and of course this is entirely possible (otherwise I would not be writing this article now would I).

This is certainly not a new thing here for Codeproject, there are numerous Docker articles,  but I never found one that talked about Cassandra, so I thought why not write another one.

 

Which I have just published here : https://www.codeproject.com/Articles/1175248/A-look-at-Docker