Category Archives: RSS Feed

Node.js Weekly Update - 19 May, 2017

By Gergely Németh

Node.js Weekly Update - 19 May, 2017

Below you can find RisingStack‘s collection of the most important Node.js news, projects, updates & security leaks from this week:

1. Launching Ignition and TurboFan in V8

Today we are excited to announce the launch of a new JavaScript execution pipeline for V8 5.9 that will reach Chrome Stable in M59. With the new pipeline, we achieve big performance improvements and significant memory savings on real-world JavaScript applications.

Node.js Weekly Update - 19 May, 2017

The new pipeline is built upon Ignition, V8’s interpreter, and TurboFan, V8’s newest optimizing compiler. These technologies should be familiar to those of you who have followed the V8 blog over the last few years, but the switch to the new pipeline marks a big new milestone for both.

2. Packing a Kubernetes Microservices App with Helm

This post shows how we packed our Kubernetes microservices app with Helm and made them easy to reproduce in various environments.

Node.js Weekly Update - 19 May, 2017

At RisingStack we use Kubernetes with tens of microservices to provide our Node.js monitoring solution for our SaaS customers. During the last couple of months, we were asked by many enterprises with strict data compliance requirements to make our product available as an on-premises solution. So we had to find a solution that makes easy for them to install Trace as a single piece of software and hides the complexity of our infrastructure.

3. How Four Native Developers Wrote An Electron App

Electron is a well-known on-ramp for web developers to build desktop apps using familiar web technologies: HTML, CSS, and JavaScript.

Node.js Weekly Update - 19 May, 2017

Our situation was different. Everyone on the GitHub Desktop team is a native developer by trade—three from the .NET world and one from Cocoa. We knew how to make native apps, so how and why did we end up here?

4. Developing Microservices – Node, React, and Docker

In this post you will learn how to quickly spin up a reproducible development environment with Docker to manage a number of Node.js microservices.

Node.js Weekly Update - 19 May, 2017

The end goal of this post is to organize the technologies from the above image into the following containers and services:

Node.js Weekly Update - 19 May, 2017

5. Sharp – High performance Node.js image processing

The typical use case for this high speed Node.js module is to convert large images in common formats to smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.

npm install sharp

Resizing an image is typically 4x-5x faster than using the quickest ImageMagick and GraphicsMagick settings. Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly. Lanczos resampling ensures quality is not sacrificed for speed.

6. Automatically Build and Publish Node and Electron Applications for Linux

This is an introduction to snapcraft.io and electron-builder, tools that enable you to deliver your Node and Electron applications to millions of Linux users.

Node.js Weekly Update - 19 May, 2017

Packaging for Linux used to be hard, but that has changed: the Snapcraft team have built a platform that makes it simple to build and publish your applications so they run on all the major Linux distributions. It gives you tools to deliver updates at a high frequency without compromising stability. Push a commit and it will automatically build for free in the store for your users.

Vulnerable npm Packages Discovered:

Medium severity

Low severity

Previously in the Node.js Weekly

In the previous Node.js Weekly Update we read about Writing Secure Node Code, Project Glimpse, Post-Mortem Diagnostics, using Docker Compose & more..

We help you to stay up-to-date with Node.js on a daily basis too. Check out our Node.js news page and its Twitter feed!

Source:: risingstack.com

Presenting Onsen UI's newest components

By Júnio Silva

Action sheet

You may have happened to read our last blog post about some of the progress we have reached lately in Onsen UI. Well surprise surprise, that’s not all.

We want to take our hybrid app framework one step further, so besides all those recent updates about stability improvement in iOS and so on, in recent version 2.3.0 we have added brand new core components that you can start using right away.

Keep reading if you’d like to get acquainted with these new components.

Action sheet

You are probably familiar with those sheets sliding up from the bottom of the screen and prompting user action, much like dialogs. From Action sheets of iOS and Bottom sheets of Android, behold the ons-action-sheet.

Coupled to ons-action-sheet there is also another new component ons-action-sheet-button that you can use to create those nice action buttons inside your action sheets. Here is a typical use case template:

<ons-action-sheet id="sheet">
  <ons-action-sheet-button icon="md-square-o">Label</ons-action-sheet-button>
  <ons-action-sheet-button icon="md-square-o" modifier="destructive">Delete Label</ons-action-sheet-button>
  <ons-action-sheet-button icon="md-close">Cancel</ons-action-sheet-button>
</ons-action-sheet>

Being a dialog it is naturally hidden by default and you need to open it programatically. For brevity I recommend you check the tutorial for an example on how to do that in the different platforms.

You can also create and open an action sheet dynamically with the method ons.openActionSheet (which btw is also done in the tutorial above). To create the same sheet as in the example above the code would be:

ons.openActionSheet({
  id: 'sheet',
  buttons: [
    {
      label: 'Label',
      icon: 'md-square-o'
    },
    {
      label: 'Delete Label',
      icon: 'md-square-o',
      modifier: 'destructive'
    },
    {
      label: 'Cancel',
      icon: 'md-close'
    }
  ]
})

Card

Want to show some nice pieces of information in a stylish fashion without too much CSS work? The component you are looking for is the new ons-card.

Card

With it you get a highlighted box in which you can insert any type of content you wish. We provide special classes title and content to make style separation even simpler. Typical use case:

<ons-card>
  <div class="title">Title</div>
  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</ons-card>

Of course a card will be richer if you add other media content such as images to it or mix it up with other Onsen UI components. We even have the open possibility of adding other predefined building blocks (e.g. the ones here) so please let us know if you think that would be nice to have.

Toast

Yet another new dialog is provided by ons-toast. Use it when you want to pass a subtler message to the user notifying him that something happened and optionally prompting for an action but allowing the normal app flow to continue.

Toast

Here is a way in which you can define a toast dialog statically:

<ons-toast>
  Message Message Message Message<button>Action</button>
</ons-toast>

Similar to the ons-action-sheet, you can simply show the above ons-toast through your JavaScript code or create the whole thing dynamically with the ons.notification.toast method. This way you may define extra options such as callback (for when the toast is hidden) or timeout (to hide it automatically after a certain time). For details please refer to the documentation.

One last thing to notice is that in case you attempt to show more than one toast at once (maybe your server replied with several notices?) there is a queue of toasts that holds them so that there’s always only one shown toast (which is in accord with the spec). Once a toast is hidden the next one in the queue (if any) is shown.

Search input

Finally since the regular HTML input with type='search' has a design that is not suited for mobile apps, we gave it a new “clothing”, as you can see below. Looks more appropriate to what you would find in a native app, right?

Search input

As usual the style is adapted automatically to the running platform.

Well that’s about it, we hope that these new components are useful to you and that you give us some feedback in case you have any (make sure to upgrade to the latest version in order to be able to use them). Feel free to reach out to us on our Gitter chat or on our Community with problems you may have and/or ideas on how to improve this (awesome) mobile UI framework.


Onsen UI is an open source library used to create the user interface of hybrid apps. You can find more information on our GitHub page. If you like Onsen UI, please don’t forget to give us a star! ★★★★★

Source:: https://onsen.io/

Build a RESTful API with Flask – The TDD Way: Part 2

By Jee Gikera

In part 1 of this series, we learnt how to create a RESTful API the TDD way.
We covered writing tests and learnt a lot about Flask.

In this part of the series, we’ll learn how to authenticate and authorize users in our API. If you haven’t read part 1, please do because this tutorial will build up on it.
In this tutorial, we’ll talk about securing our API with token-based authentication and user authorization. We will integrate users into the API we built in part 1.

In order to get started, ensure your virtual environment is activated.

The User Model

We intend to allow bucketlists to be owned by users. For now, anyone can manipulate a bucketlist even if they did not create it. We’ve got to fix this security hole.
How do we keep track of users, you ask? We define a model.

# app/models.py

from app import db
from flask_bcrypt import Bcrypt

class User(db.Model):
    """This class defines the users table """

    __tablename__ = 'users'

    # Define the columns of the users table, starting with the primary key
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(256), nullable=False, unique=True)
    password = db.Column(db.String(256), nullable=False)
    bucketlists = db.relationship(
        'Bucketlist', order_by='Bucketlist.id', cascade="all, delete-orphan")

    def __init__(self, email, password):
        """Initialize the user with an email and a password."""
        self.email = email
        self.password = Bcrypt().generate_password_hash(password).decode()

    def password_is_valid(self, password):
        """
        Checks the password against it's hash to validates the user's password
        """
        return Bcrypt().check_password_hash(self.password, password)

    def save(self):
        """Save a user to the database.
        This includes creating a new user and editing one.
        """
        db.session.add(self)
        db.session.commit()

class Bucketlist(db.Model):
    """This class defines the bucketlist table."""

    __tablename__ = 'bucketlists'

    # define the columns of the table, starting with its primary key
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(
        db.DateTime, default=db.func.current_timestamp(),
        onupdate=db.func.current_timestamp())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id))

    def __init__(self, name, created_by):
        """Initialize the bucketlist with a name and its creator."""
        self.name = name
        self.created_by = created_by

    def save(self):
        """Save a bucketlist.
        This applies for both creating a new bucketlist
        and updating an existing onupdate
        """
        db.session.add(self)
        db.session.commit()

    @staticmethod
    def get_all(user_id):
        """This method gets all the bucketlists for a given user."""
        return Bucketlist.query.filter_by(created_by=user_id)

    def delete(self):
        """Deletes a given bucketlist."""
        db.session.delete(self)
        db.session.commit()

    def __repr__(self):
        """Return a representation of a bucketlist instance."""
        return "<Bucketlist: {}>".format(self.name)

Here’s what we’ve done:

  • We imported Flask-Bcrypt extension to help us in hashing our passwords. You should never store passwords in plaintext.
  • We created a User model that represents the users table. It contains the email and password fields to capture the user’s credentials.
  • Since a user can own many bucketlists, we defined a One-to-Many relationship between the two tables. We defined this relationship by adding the db.relationship() function on the User table (parent table)
  • We added a foreign key on the child table (Bucketlist) referencing the User table. The foreign key has some arguments. The cascade="all, delete-orphan" will delete all bucketlists when a referenced user is deleted.
  • We hash the password by using generate_password_hash(pasword). This will make our users password be secure from dictionary and brute force attacks.
  • We refactored the get_all() method to get all the bucketlists for a given user.

Don’t forget to install Flask-Bcrypt

(venv)$ pip install flask-bcrypt

Migrate them

Migrate the changes we’ve just made to the db we initially created in part 1 of the series.

(venv)$    python manage.py db migrate
(venv)$    python manage.py db upgrade

Now we have a user table to keep track of registered users.

Automate Tests

Our app will have many tests from now on. It’s best practice to have a test folder that will houses all our tests. We’ll create a folder called tests. Inside this folder, we’ll move our test_bucketlists.py file into it.
Our directory structure should now look like this:

├── bucketlist
    ├── app
    │   ├── __init__.py
    │   └── models.py  
    ├── instance
    │   ├── __init__.py
    │   └── config.py
    ├── manage.py
    ├── requirements.txt
    ├── run.py
    ├── tests
    │   └── test_bucketlist.py

Also, we’ll edit the manage.py as follows:

import os
import unittest
# class for handling a set of commands
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import db, create_app

# initialize the app with all its configurations
app = create_app(config_name=os.getenv('APP_SETTINGS'))
migrate = Migrate(app, db)
# create an instance of class that will handle our commands
manager = Manager(app)

# Define the migration command to always be preceded by the word "db"
# Example usage: python manage.py db init
manager.add_command('db', MigrateCommand)

# define our command for testing called "test"
# Usage: python manage.py test
@manager.command
def test():
    """Runs the unit tests without test coverage."""
    tests = unittest.TestLoader().discover('./tests', pattern='test*.py')
    result = unittest.TextTestRunner(verbosity=2).run(tests)
    if result.wasSuccessful():
        return 0
    return 1

if __name__ == '__main__':
    manager.run()

The decorator on top of test() allows us to define a command called test. Inside the function, we load the tests from the tests folder using the TestLoader() class and then run them with TextTestRunner.run(). If it’s successful, we exit gracefully with a return 0.

Let’s test it out on our terminal.

(venv)$   python manage.py test

The tests should fail. This is because we’ve not modified our code to work with the new changes in the model.
From now on, we’ll use this command to run our tests.

Token-based authentication

Token-based authentication is a security technique that authenticates users who attempt to login to a server using a security token provided by the server. Without the token, a user won’t be granted access to restricted resources. You can find more intricate details about token based authentication here

For us to implement this authentication, we’ll use a Python package called PyJWT.
PyJWT allows us to encode and decode JSON Web Tokens (JWT). That being said, let’s install it:

(venv)$  pip install PyJWT

Securing Requests

For our users to authenticate, the access token is going to be placed in the Authorization HTTP header in all our bucketlist requests.

Here’s how the header looks like:

Authorization:  "Bearer <The-access-token-is-here>"

We’ll put the word Bearer before the token and separate them with a space character.
Don’t forget the space in between the Bearer and the token.

Encode and Decode the Token

We need to create a way to encode the token before it’s sent to the user. We also need to have a way to decode the token when the user sends it via the Authorization header.

In our model.py we’ll create a function inside our User model to generate the token and another one to decode it. Let’s add the following code:

# /app/models.py

## previous  imports ###
import jwt
from datetime import datetime, timedelta

class User(db.Model):
    """Maps to users table """

    __tablename__ = 'users'

    ###########################################
    ## Existing code for defining table columns is here  ##
    ###########################################

    def __init__(self, email, password):
        #### INIT CODE LIES HERE ###################
        ###########################################

    def password_is_valid(self, password):
        ##### PASSWORD CHECK CODE LIES HERE ####
        ###########################################

    def save(self):
        ######### CODE FOR SAVING USER LIES HERE ##
        ############################################

    def generate_token(self, user_id):
        """ Generates the access token"""

        try:
            # set up a payload with an expiration time
            payload = {
                'exp': datetime.utcnow() + timedelta(minutes=5),
                'iat': datetime.utcnow(),
                'sub': user_id
            }
            # create the byte string token using the payload and the SECRET key
            jwt_string = jwt.encode(
                payload,
                current_app.config.get('SECRET'),
                algorithm='HS256'
            )
            return jwt_string

        except Exception as e:
            # return an error in string format if an exception occurs
            return str(e)

    @staticmethod
    def decode_token(token):
        """Decodes the access token from the Authorization header."""
        try:
            # try to decode the token using our SECRET variable
            payload = jwt.decode(token, current_app.config.get('SECRET'))
            return payload['sub']
        except jwt.ExpiredSignatureError:
            # the token is expired, return an error string
            return "Expired token. Please login to get a new token"
        except jwt.InvalidTokenError:
            # the token is invalid, return an error string
            return "Invalid token. Please register or login"

The generate_token() takes in a user ID as an argument, uses jwt to create a token using the secret key, and makes it time-based by defining its expiration time. The token is valid for 5 minutes as specified in the timedelta. You can set it to your liking.

The decode_token() takes in a token as an argument and checks whether the token is valid. If it is, it returns the user ID as the payload. It returns an error messsage if the token is expired or invalid.

Don’t forget to import jwt and the datetime above.

The Auth Blueprint

Our app is growing bigger. We’ll have to organize it into components. Flask uses the concept of Blueprints to make application components.

Blueprints are simply a set of operations that can be registered on a given app. Think of it as an extension of the app that can address a specific functionality.

We’ll create an authentication blueprint.
This blueprint will focus on handling user registration and logins.

Inside our /app directory create a folder and call it auth.
Our auth folder should contain:

  • __init__.py file
  • views.py file

In our auth/__init__.py file, initialize a blueprint.

# auth/__init__.py

from flask import Blueprint

# This instance of a Blueprint that represents the authentication blueprint
auth_blueprint = Blueprint('auth', __name__)

from . import views

Then import the blueprint and register it at the bottom of the app/__init__.py, just before the return app line.

# app/__init__.py

# imports lie here

def create_app(config_name):
    #####################################################
    ### Existing code for intializing the app with its configurations  ###
    #####################################################

    @app.route('/bucketlists/<int:id>', methods=['GET', 'PUT', 'DELETE'])
    def bucketlist_manipulation(id, **kwargs):
           #########################################################
        ### Existing code for creating, updating and deleting a bucketlist #####
        #########################################################
        ...

    # import the authentication blueprint and register it on the app
    from .auth import auth_blueprint
    app.register_blueprint(auth_blueprint)

    return app

Test First, Implement Later

Testing should never be an afterthought. It should always come first.
We’re going to add a new test file that will house all our tests for the authentication blueprint.
It’ll test whether our API can handle user registration, user login and access-token generation.

In our tests directory, create a file naming it test_auth.py. Write the following code in it:

# /tests/test_auth.py

import unittest
import json
from app import create_app, db

class AuthTestCase(unittest.TestCase):
    """Test case for the authentication blueprint."""

    def setUp(self):
        """Set up test variables."""
        self.app = create_app(config_name="testing")
        # initialize the test client
        self.client = self.app.test_client
        # This is the user test json data with a predefined email and password
        self.user_data = {
            'email': 'test@example.com',
            'password': 'test_password'
        }

        with self.app.app_context():
            # create all tables
            db.session.close()
            db.drop_all()
            db.create_all()

    def test_registration(self):
        """Test user registration works correcty."""
        res = self.client().post('/auth/register', data=self.user_data)
        # get the results returned in json format
        result = json.loads(res.data.decode())
        # assert that the request contains a success message and a 201 status code
        self.assertEqual(result['message'], "You registered successfully.")
        self.assertEqual(res.status_code, 201)

    def test_already_registered_user(self):
        """Test that a user cannot be registered twice."""
        res = self.client().post('/auth/register', data=self.user_data)
        self.assertEqual(res.status_code, 201)
        second_res = self.client().post('/auth/register', data=self.user_data)
        self.assertEqual(second_res.status_code, 202)
        # get the results returned in json format
        result = json.loads(second_res.data.decode())
        self.assertEqual(
            result['message'], "User already exists. Please login.")

We’ve initialized our test with a test client for making requests to our API and some test data.
The first test function test_registration() sends a post request to /auth/register and tests the response it gets. It ensures that the status code is 201, meaning we’ve successfully created a user.
The second test function tests whether the API can only register a user once. Having duplicates in the database is bad for business.

Now let’s run the tests using python manage.py test. The tests should fail.

----------------------------------------------------------------------
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

User Registration View

The reason our tests fail is simply because we lack the functionality they need to test. Let’s implement something that’ll make these two tests to pass.

Open up the views.py file and add the following code:

# /app/auth/views.py

from . import auth_blueprint

from flask.views import MethodView
from flask import make_response, request, jsonify
from app.models import User

class RegistrationView(MethodView):
    """This class registers a new user."""

    def post(self):
        """Handle POST request for this view. Url ---> /auth/register"""

        # Query to see if the user already exists
        user = User.query.filter_by(email=request.data['email']).first()

        if not user:
            # There is no user so we'll try to register them
            try:
                post_data = request.data
                # Register the user
                email = post_data['email']
                password = post_data['password']
                user = User(email=email, password=password)
                user.save()

                response = {
                    'message': 'You registered successfully. Please log in.'
                }
                # return a response notifying the user that they registered successfully
                return make_response(jsonify(response)), 201
            except Exception as e:
                # An error occured, therefore return a string message containing the error
                response = {
                    'message': str(e)
                }
                return make_response(jsonify(response)), 401
        else:
            # There is an existing user. We don't want to register users twice
            # Return a message to the user telling them that they they already exist
            response = {
                'message': 'User already exists. Please login.'
            }

            return make_response(jsonify(response)), 202

registration_view = RegistrationView.as_view('register_view')
# Define the rule for the registration url --->  /auth/register
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    '/auth/register',
    view_func=registration_view,
    methods=['POST'])

Here’s what we have added:

  • We imported our blueprint together with Flask’s make_response (for returning our response) and jsonify (for encoding our data in JSON and adding a application/json header to the response)
  • We’ve defined a class-based view to handle registration by dispatching a POST request to our post() method.
  • Inside our post() method, we check if the user exists in our database. If they don’t, we create a new user and return a message to them notifying their successful registration.
    If the user exists they are reminded to login.
  • Lastly, we used as_view() method to make our class-based view callable so that it can take a request and return a response. We then defined the url for registering a user as /auth/register.

Let’s run our tests once more. Only the AuthTestCase tests should pass. The bucketlist tests still fail because we haven`t modified the __init__.py code.

test_already_registered_user (test_auth.AuthTestCase)
Test that a user cannot be registered twice. ... ok
test_registration (test_auth.AuthTestCase)
Test user registration works correcty. ... ok

Bucketlist failed tests fall here
----------------------------------------------------------------------

(venv)

Using Postman [ /auth/register ]

We’ll test our registration functionality by making a request using Postman.
But before we make the requests, ensure the API is up and running.

(venv) $ python run.py development

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 225-021-817

Now you can make a POST request to localhost:5000/auth/register. Specify an email and a password of your choice to represent the user we are registering. Click send.

User Login

A user will have to login to gain access to our API. Currently, we are lacking this login functionality. Let’s start with some tests. We’ll add two more tests at the bottom of our test_auth.py as follows:

# tests/test_auth.py
class AuthTestCase(unittest.TestCase):
    """Test case for the authentication blueprint."""

    def setUp(self):
        #### EXISTING CODE FOR SETUP LIES HERE ####

    def test_registration(self):
        #### EXISTING TEST CODE LIES HERE ####

    def test_already_registered_user(self):
        ### EXISTING TEST CODE LIES HERE #####

    def test_user_login(self):
        """Test registered user can login."""
        res = self.client().post('/auth/register', data=self.user_data)
        self.assertEqual(res.status_code, 201)
        login_res = self.client().post('/auth/login', data=self.user_data)

        # get the results in json format
        result = json.loads(login_res.data.decode())
        # Test that the response contains success message
        self.assertEqual(result['message'], "You logged in successfully.")
        # Assert that the status code is equal to 200
        self.assertEqual(login_res.status_code, 200)
        self.assertTrue(result['access_token'])

    def test_non_registered_user_login(self):
        """Test non registered users cannot login."""
        # define a dictionary to represent an unregistered user
        not_a_user = {
            'email': 'not_a_user@example.com',
            'password': 'nope'
        }
        # send a POST request to /auth/login with the data above
        res = self.client().post('/auth/login', data=not_a_user)
        # get the result in json
        result = json.loads(res.data.decode())

        # assert that this response must contain an error message 
        # and an error status code 401(Unauthorized)
        self.assertEqual(res.status_code, 401)
        self.assertEqual(
            result['message'], "Invalid email or password, Please try again")

The test_user_login() function tests whether our API can successfully login a registered user. It also tests for the access token.

The other test function test_non_registered_user_login() tests whether our API can restrict signing in to only registered users.

Login View

Again, we’ll make the tests pass by implementing its functionality. Let’s create the login view.

from . import auth_blueprint

from flask.views import MethodView
from flask import Blueprint, make_response, request, jsonify
from app.models import User

class RegistrationView(MethodView):
    """This class-based view registers a new user."""
    #### EXISTING REGISTRATION CODE HERE ####
    ##########################################

class LoginView(MethodView):
    """This class-based view handles user login and access token generation."""

    def post(self):
        """Handle POST request for this view. Url ---> /auth/login"""
        try:
            # Get the user object using their email (unique to every user)
            user = User.query.filter_by(email=request.data['email']).first()

            # Try to authenticate the found user using their password
            if user and user.password_is_valid(request.data['password']):
                # Generate the access token. This will be used as the authorization header
                access_token = user.generate_token(user.id)
                if access_token:
                    response = {
                        'message': 'You logged in successfully.',
                        'access_token': access_token.decode()
                    }
                    return make_response(jsonify(response)), 200
            else:
                # User does not exist. Therefore, we return an error message
                response = {
                    'message': 'Invalid email or password, Please try again'
                }
                return make_response(jsonify(response)), 401

        except Exception as e:
            # Create a response containing an string error message
            response = {
                'message': str(e)
            }
            # Return a server error using the HTTP Error Code 500 (Internal Server Error)
            return make_response(jsonify(response)), 500

# Define the API resource
registration_view = RegistrationView.as_view('registration_view')
login_view = LoginView.as_view('login_view')

# Define the rule for the registration url --->  /auth/register
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    '/auth/register',
    view_func=registration_view,
    methods=['POST'])

# Define the rule for the registration url --->  /auth/login
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    '/auth/login',
    view_func=login_view,
    methods=['POST']
)

Here, we’ve defined a class-based view just like we did on the registration section.
It dispatches the POST request to the post() method as well. This is to capture the user credentials(email, password) when they login. It checks whether the password given is valid, generates an access token for the user and returns a response containing the token.
We’ve also handled exceptions gracefully so that if one occurs, our API will continue running and won’t crush.
Finally, we defined a url for the login route.

Logging in on Postman [ /auth/login ]

Make a POST request. Input the email and password we specified for the user during registration. Click send. You should get an access token in the JSON response.

Running the tests

If you run the tests, you will notice that the login tests pass, but the bucketlist one still fail. It’s time to refactor this tests.

Refactor Bucketlist Tests

First, we’ll create two helper functions for registering and signing in our test user.

# tests/test_bucketlist.py
class BucketlistTestCase(unittest.TestCase):
    """This class represents the bucketlist test case"""

    def setUp(self):
        """Set up test variables."""
     #### SETUP VARIABLES ARE HERE #####
     ####################################

    def register_user(self, email="user@test.com", password="test1234"):
        """This helper method helps register a test user."""
        user_data = {
            'email': email,
            'password': password
        }
        return self.client().post('/auth/register', data=user_data)

    def login_user(self, email="user@test.com", password="test1234"):
        """This helper method helps log in a test user."""
        user_data = {
            'email': email,
            'password': password
        }
        return self.client().post('/auth/login', data=user_data)

    ############################################
    ##### ALL OUR TESTS METHODS LIE HERE #######

# Make the tests conveniently executable
if __name__ == "__main__":
    unittest.main()

We do this so that when we want to register or login a test user (which is in all the tests), we don’t have to repeat ourselves. We’ll simply call the function and we are set.

Next, we’ll define a way to get the access token and add it to the Authorization header in all our client requests. Here’s a code snippet of the how were going to do it.

    def test_bucketlist_creation(self):
        """Test the API can create a bucketlist (POST request)"""
        # register a test user, then log them in
        self.register_user():
        result = self.login_user()
        # obtain the access token
        access_token = json.loads(result.data.decode())['access_token']

        # ensure the request has an authorization header set with the access token in it
        res = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)

We can now go ahead and refactor the whole test_bucketlist.py file. After refactoring all our request, we should have something like this:

import unittest
import os
import json
from app import create_app, db

class BucketlistTestCase(unittest.TestCase):
    """This class represents the bucketlist test case"""

    def setUp(self):
        """Define test variables and initialize app."""
        self.app = create_app(config_name="testing")
        self.client = self.app.test_client
        self.bucketlist = {'name': 'Go to Borabora for vacay'}

        # binds the app to the current context
        with self.app.app_context():
            # create all tables
            db.session.close()
            db.drop_all()
            db.create_all()

    def register_user(self, email="user@test.com", password="test1234"):
        user_data = {
            'email': email,
            'password': password
        }
        return self.client().post('/auth/register', data=user_data)

    def login_user(self, email="user@test.com", password="test1234"):
        user_data = {
            'email': email,
            'password': password
        }
        return self.client().post('/auth/login', data=user_data)

    def test_bucketlist_creation(self):
        """Test API can create a bucketlist (POST request)"""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())['access_token']

        # create a bucketlist by making a POST request
        res = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)
        self.assertEqual(res.status_code, 201)
        self.assertIn('Go to Borabora', str(res.data))

    def test_api_can_get_all_bucketlists(self):
        """Test API can get a bucketlist (GET request)."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())['access_token']

        # create a bucketlist by making a POST request
        res = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)
        self.assertEqual(res.status_code, 201)

        # get all the bucketlists that belong to the test user by making a GET request
        res = self.client().get(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
        )
        self.assertEqual(res.status_code, 200)
        self.assertIn('Go to Borabora', str(res.data))

    def test_api_can_get_bucketlist_by_id(self):
        """Test API can get a single bucketlist by using it's id."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())['access_token']

        rv = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)

        # assert that the bucketlist is created 
        self.assertEqual(rv.status_code, 201)
        # get the response data in json format
        results = json.loads(rv.data.decode())

        result = self.client().get(
            '/bucketlists/{}'.format(results['id']),
            headers=dict(Authorization="Bearer " + access_token))
        # assert that the bucketlist is actually returned given its ID
        self.assertEqual(result.status_code, 200)
        self.assertIn('Go to Borabora', str(result.data))

    def test_bucketlist_can_be_edited(self):
        """Test API can edit an existing bucketlist. (PUT request)"""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())['access_token']

        # first, we create a bucketlist by making a POST request
        rv = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data={'name': 'Eat, pray and love'})
        self.assertEqual(rv.status_code, 201)
        # get the json with the bucketlist
        results = json.loads(rv.data.decode())

        # then, we edit the created bucketlist by making a PUT request
        rv = self.client().put(
            '/bucketlists/{}'.format(results['id']),
            headers=dict(Authorization="Bearer " + access_token),
            data={
                "name": "Dont just eat, but also pray and love :-)"
            })
        self.assertEqual(rv.status_code, 200)

        # finally, we get the edited bucketlist to see if it is actually edited.
        results = self.client().get(
            '/bucketlists/{}'.format(results['id']),
            headers=dict(Authorization="Bearer " + access_token))
        self.assertIn('Dont just eat', str(results.data))

    def test_bucketlist_deletion(self):
        """Test API can delete an existing bucketlist. (DELETE request)."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())['access_token']

        rv = self.client().post(
            '/bucketlists/',
            headers=dict(Authorization="Bearer " + access_token),
            data={'name': 'Eat, pray and love'})
        self.assertEqual(rv.status_code, 201)
        # get the bucketlist in json
        results = json.loads(rv.data.decode())

        # delete the bucketlist we just created
        res = self.client().delete(
            '/bucketlists/{}'.format(results['id']),
            headers=dict(Authorization="Bearer " + access_token),)
        self.assertEqual(res.status_code, 200)

        # Test to see if it exists, should return a 404
        result = self.client().get(
            '/bucketlists/1',
            headers=dict(Authorization="Bearer " + access_token))
        self.assertEqual(result.status_code, 404)

# Make the tests conveniently executable
if __name__ == "__main__":
    unittest.main()

Refactor GET(all) and POST functionality

We’ll refactor the methods that handle the HTTP requests for bucketlist creation and getting all the bucketlists. Open up /app/__init__.py file and edit as follows:

# /app/__init__.py

## imports ##
from flask import request, jsonify, abort, make_response

def create_app(config_name):
    from models import Bucketlist, User

    ###########################################
    ### EXISTING APP CONFIG CODE LIES HERE ###
    ###########################################

    @app.route('/bucketlists/', methods=['POST', 'GET'])
    def bucketlists():
        # Get the access token from the header
        auth_header = request.headers.get('Authorization')
        access_token = auth_header.split(" ")[1]

        if access_token:
         # Attempt to decode the token and get the User ID
            user_id = User.decode_token(access_token)
            if not isinstance(user_id, str):
                # Go ahead and handle the request, the user is authenticated

                if request.method == "POST":
                    name = str(request.data.get('name', ''))
                    if name:
                        bucketlist = Bucketlist(name=name, created_by=user_id)
                        bucketlist.save()
                        response = jsonify({
                            'id': bucketlist.id,
                            'name': bucketlist.name,
                            'date_created': bucketlist.date_created,
                            'date_modified': bucketlist.date_modified,
                            'created_by': user_id
                        })

                        return make_response(response), 201

                else:
                    # GET all the bucketlists created by this user
                    bucketlists = Bucketlist.query.filter_by(created_by=user_id)
                    results = []

                    for bucketlist in bucketlists:
                        obj = {
                            'id': bucketlist.id,
                            'name': bucketlist.name,
                            'date_created': bucketlist.date_created,
                            'date_modified': bucketlist.date_modified,
                            'created_by': bucketlist.created_by
                        }
                        results.append(obj)

                    return make_response(jsonify(results)), 200
            else:
                # user is not legit, so the payload is an error message
                message = user_id
                response = {
                    'message': message
                }
                return make_response(jsonify(response)), 401

We first added two imports: the User model and the make_response from Flask.
In the bucketlist function, we check for the authorization header from the request and extract the access token. Then, we decoded the token using User.decode_token(token) to give us the payload. The payload is expected to be a user ID if the token is valid and not expired. If the token is not valid or expired, the payload will be an error message as a string.

Test it on Postman

Create a bucketlist or two

Copy the token and paste it to the header section, creating an Authorization header. Don’t forget to put the word Bearer before the token with a space separating them like this:

Authorization: "Bearer dfg32r22349r40eiwoijr232394029wfopi23r2.2342..."

Make a POST request to localhost:5000/bucketlists/, specifying the name of the bucketlist. Click send.

Get all bucketlists for a given user

Ensure you’ve set the Authorization header just as we did for the POST request.

Make a GET request to localhost:5000/bucketlists/ and retrieve all the bucketlists our user just created.

Finally, Refactor GET(one), PUT and DELETE functionality

We’ll refactor the PUT and DELETE functionality the same way we tackled the GET and POST.


# /app/__init__.py

## imports ##
from flask import request, jsonify, abort, make_response

def create_app(config_name):
    from models import Bucketlist, User

    ############################################################
    ### Existing code for initializing the app with its configurations lies here ###
    ############################################################

    @app.route('/bucketlists/', methods=['POST', 'GET'])
    def bucketlists():
        #### CODE FOR  GET and POST LIES HERE#####
        ###############################

    @app.route('/bucketlists/<int:id>', methods=['GET', 'PUT', 'DELETE'])
    def bucketlist_manipulation(id, **kwargs):
        # get the access token from the authorization header
        auth_header = request.headers.get('Authorization')
        access_token = auth_header.split(" ")[1]

        if access_token:
            # Get the user id related to this access token
            user_id = User.decode_token(access_token)

            if not isinstance(user_id, str):
                # If the id is not a string(error), we have a user id
                # Get the bucketlist with the id specified from the URL (<int:id>)
                bucketlist = Bucketlist.query.filter_by(id=id).first()
                if not bucketlist:
                    # There is no bucketlist with this ID for this User, so
                    # Raise an HTTPException with a 404 not found status code
                    abort(404)

                if request.method == "DELETE":
                    # delete the bucketlist using our delete method
                    bucketlist.delete()
                    return {
                        "message": "bucketlist {} deleted".format(bucketlist.id)
                    }, 200

                elif request.method == 'PUT':
                    # Obtain the new name of the bucketlist from the request data
                    name = str(request.data.get('name', ''))

                    bucketlist.name = name
                    bucketlist.save()

                    response = {
                        'id': bucketlist.id,
                        'name': bucketlist.name,
                        'date_created': bucketlist.date_created,
                        'date_modified': bucketlist.date_modified,
                        'created_by': bucketlist.created_by
                    }
                    return make_response(jsonify(response)), 200
                else:
                    # Handle GET request, sending back the bucketlist to the user
                    response = {
                        'id': bucketlist.id,
                        'name': bucketlist.name,
                        'date_created': bucketlist.date_created,
                        'date_modified': bucketlist.date_modified,
                        'created_by': bucketlist.created_by
                    }
                    return make_response(jsonify(response)), 200
            else:
                # user is not legit, so the payload is an error message
                message = user_id
                response = {
                    'message': message
                }
                # return an error response, telling the user he is Unauthorized
                return make_response(jsonify(response)), 401

    # import the authentication blueprint and register it on the app
    from .auth import auth_blueprint
    app.register_blueprint(auth_blueprint)

    return app

Running python manage.py test should now yield passing tests.

test_already_registered_user (test_auth.AuthTestCase)
Test that a user cannot be registered twice. ... ok
test_non_registered_user_login (test_auth.AuthTestCase)
Test non registered users cannot login. ... ok
test_registration (test_auth.AuthTestCase)
Test user registration works correcty. ... ok
test_user_login (test_auth.AuthTestCase)
Test registered user can login. ... ok
test_api_can_get_all_bucketlists (test_bucketlist.BucketlistTestCase)
Test API can get a bucketlist (GET request). ... ok
test_api_can_get_bucketlist_by_id (test_bucketlist.BucketlistTestCase)
Test API can get a single bucketlist by using it's id. ... ok
test_bucketlist_can_be_edited (test_bucketlist.BucketlistTestCase)
Test API can edit an existing bucketlist. (PUT request) ... ok
test_bucketlist_creation (test_bucketlist.BucketlistTestCase)
Test API can create a bucketlist (POST request) ... ok
test_bucketlist_deletion (test_bucketlist.BucketlistTestCase)
Test API can delete an existing bucketlist. (DELETE request). ... ok

----------------------------------------------------------------------
Ran 9 tests in 1.579s

OK
(venv)

Now let’s test to see if it works on Postman.
Fire up the API using python run.py development

Make a GET request for a single bucketlist to localhost:5000/bucketlists/2

Feel free to play around with the PUT and DELETE functionality.

Conclusion

We’ve covered quite a lot on securing our API. We went through defining a user model and integrating users into our API. We also covered token-based authentication and used an authentication blueprint to implement it.

Even though our main focus is to write the code, we should not let testing be an afterthought.
For us to improve on code quality, there has to be tests. Testing is the secret to increasing the agility of your product development. In everything project you do, put TTD first.

If you’ve coded this to the end, you are awesome!

Feel free to recommend this to friends and colleagues.

Source:: scotch.io

Creative Splash Transition with CSS and SVG

By Luis Manuel

SVG path’s are really awesome! And its versatility is what makes them even more impressive and useful for creating engaging animations.

In this tutorial we’ll be creating an eye catching animation, just using SVG paths and CSS transitions. To make things easier, we will also be using Pug and Sass, HTML and CSS preprocessors respectively. They will allow us to write much cleaner and more organized code, and also help with maths.

The animation we’ll be creating, has been inspired by this shot on Dribbble, by Clément Brichon. More specifically, we’ll be building something like this:

The main idea to get it working in the browser, is to have a background image, and then the entire black background is composed by SVG paths that we can animate to achieve an effect similar to the animated prototype.

Important: Please note that we are using some techniques that are not fully supported by all browsers. The final demo has been tested successfully in Chrome, Opera and Firefox (Linux).

Getting started with SVG paths drawing

To implement the animation, we will use the technique of “drawing” SVG paths. You can get started with the technique reading any of this great tutorials:

However, this time we will be using a variation of this technique, so that we will not animate the stroke-dashoffset property, but the stroke-dasharray. This will provide us a little more flexibility to draw the paths in the direction we want. If you want to know more about how this technique works, you can read this tutorial that I wrote some time ago, where it is described in detail (section “Drawing from begin to end”).

Generating the HTML with Pug

Let’s start by saying that Pug (formally known as Jade) is a high performance template engine, heavily influenced by Haml and implemented with JavaScript for Node.js and browsers. If you want to learn more about it, here is a nice tutorial.

Pug’s syntax is pretty straightforward, and we will be using just variables and mixins, leaving HTML markup as is. So you may not need to read a tutorial to understand the following code 🙂

//- Background image and button
<div class="background-image"></div>
<button>Toggle Animation</button>

//- Some variables to help with maths
- var circlesNumber = 5;
- var strokeWidth = 150;
- var radius = 100;
- var width = radius * 2;
- var height = radius * 2;
- var left = width / 2;
- var top = height / 2;

//- The main function to build a circle (SVG path)
//- Formula taken from this answer: http://stackoverflow.com/a/10477334
mixin circle(className)
    <path class="#{className}" d="M #{left} #{top} m -#{radius} 0 a #{radius} #{radius} 0 1 0 #{radius * 2} 0 a #{radius} #{radius} 0 1 0 -#{radius * 2} 0"/>

//- Center circle
<svg class="svg-center" width="#{width}" height="#{height}">
    + circle('path-center')
</svg>

//- Reset some variables to build the other circles
- width = 10000;
- height = 5000;
- left = width / 2;
- top = height / 2;
- radius += strokeWidth / 2 - 1;

//- Very big SVG
<svg class="svg-borders" width="#{width}" height="#{height}">

    //- Build the circles, increasing the radius at each iteration
    while circlesNumber--
        + circle('path-borders')
        - radius += strokeWidth - 1;
</svg>

Basically, what we have done here is to generate several SVG paths as circles. Then with a little style, we will make the stroke of each path very thick, covering the whole screen as we want.

Drawing the SVG paths

Now that we have our markup ready, we can start animating the SVG paths with CSS. So without further ado let’s see the code needed to get it working:

// Same variables used in HTML
$circlesNumber: 6;
$strokeWidth: 150;
$radius: 100;

// PI value (approximately)
$pi: 3.15;

// A big length
$maxLen: 10000;

// Loop to generate convenient stroke-* values
@while $circlesNumber > 0 {

  // Calculate the SVG path length (approximately)
  $currentRadius: $radius + ($circlesNumber) * $strokeWidth - $strokeWidth / 2;
  $len: 2 * $pi * $currentRadius;

  // Draw the entire path
  .path-borders:nth-child(#{$circlesNumber}) {
    stroke-dashoffset: $maxLen;
    stroke-dasharray: $maxLen 0 $len;
  }

  .open-state {

    // "Erase" the path, with alternating direction
    .path-borders:nth-child(#{$circlesNumber}) {
      @if ($circlesNumber % 2 == 0) {
        stroke-dasharray: $maxLen 0 0;
      } @else {
        stroke-dasharray: $maxLen $len $len;
      }
    }
  }

  // Next iteration
  $circlesNumber: $circlesNumber - 1;
}

To make things clearer, let’s have a look a the CSS output just for one iteration:

.path-borders:nth-child(2) {
  stroke-dashoffset: 10000;
  stroke-dasharray: 10000 0 2047.5;
}

.open-state .path-borders:nth-child(2) {
  stroke-dasharray: 10000 0 0;
}

That’s all the CSS needed to get a single SVG path working! The complexity in the SCSS code is associated with calculating these values automatically, avoid repeating code and facilitate maintenance.

Adding some animation details

To get the most of our animation, we need to pay attention to details. So, let’s apply some more effects to complement the drawing animation.

.open-state {

  // Scaling down the image
  .background-image {
    transform: scale(1);
  }

  // Rotating SVG paths (except the center circle)
  .svg-borders {
    transform: translate(-50%, -50%) rotate(90deg);
  }

  // Animating the center circle
  .path-center {
    stroke-width: 400px;
  }
}

Finishing

And we are done! With some more style touches we should get the desired effect. The live version is here. Enjoy it!

Please note that to focus the tutorial on how to build and animate SVG paths, some details in the code have been omitted. As always, we invite you to get the full code on Github, and we really hope you find it useful!

Source:: scotch.io

Acuity Scheduling, Webhooks, and Keeping Your ENTIRE Business On Schedule

By Chris Sevilleja

You run your own business. It takes a lot of time and a lot of planning. A lot of planning out your time, too. Then those pesky clients come along and want to make appointments, and if you want to make any money you have to let them. That adds a whole new wrinkle to your time management train, and soon the whole thing could come flying off the tracks.

You know, of course, that there are plenty of scheduling apps and services out there. But who has the time to test them all out, let alone get one up and running, and take the time to transfer data from your scheduling app to your other organizational tools? Getting your time under control can easily become a full-time job, and you already have one of those—that’s the whole point!

Lucky for you—and all the rest of us—Acuity Scheduling pairs full-function scheduling prowess with unstoppable integration capabilities, giving you an automated, responsive, and comprehensive way to manage your time and your business. All this is possible thanks to Webhooks.

How Webhooks Keep Your Schedule Current, Visible, and Up-to-Date

I won’t go into detail here about how Acuity’s basic scheduling features work. You can see them at work for yourself in minutes (literally, like 2-3 minutes. Even you have that much time to spare, especially since it can save you days of your life down the line…and it just might save your business, too). Simply put, both you and your clients get real-time access to your current calendar, so they can book appointments with ease and you don’t have to worry about double-bookings.

If this was all Acuity accomplished, it would be a decent addition to your business. But its scheduling interface and calendar-tracking are just the tip of the iceberg. It can also automatically update virtually any other web app you use in your business’s front- or back-end using a nifty little tool known as webhooks.

Whether you’re a skilled developer or a total novice, there are plenty of ways to put webhooks to use. You can use some of Acuity’s ready-made solutions to connect to other apps, use third-party webhook makers like Zapier to connect Acuity to additional apps and services, and craft custom solutions with your own code (or pay a developer to build these relatively simple code snippets for you).

However you decide to go, your webhooks will work essentially like a push notifications. When an app on your smartphone receives new data in response to a message, a certain amount of time passing, an event occurring, etc., it sends a push notification that lets you know exactly what’s happening. Webhooks work the same way, but their “push notifications” communicate with servers instead of smartphones and users.

Here’s a look at the basic JSON request/response for creating a new webhook subscription, which when implemented will trigger a notification from Acuity to the target URL when an appointment is made or changed:

The “event” or “action” variable can take the following values:

  • scheduled: when an appointment is made for the first time
  • rescheduled: when an appointment is moved to a different day and/or time
  • canceled: when an existing appointment is cancelled without being rescheduled
  • changed: when an appointment is changed in any way (including scheduled, rescheduled, and cancelled, and/or if an associated email address, name, phone number, or other details are altered)

The “id” variable is unique to each appointment and automatically updated and assigned by Acuity, for easy tracking of appointments and their associated details. The “calendarID” and “appointmentTypeID” variables allow you to organize different multiple calendars, differentiate between client appointments and meetings with investors or partners, or have any other differentiating criteria you want to apply to your scheduling and time management.

When the assigned URL receives the webhook notification, it can perform pretty much any action you can code. Create a custom webpage to show your customer their personalized appointment details, send yourself an email notice about the new appointment, make an automated phone call, update spreadsheets with any or all of your appointment-related data, interface with your accounting software, generate an invoice…

The possibilities are truly as limitless as the power of the web. If you can make it happen in PHP, JavaScript, Ruby, or any other POST-ready language, you can trigger it with an Acuity webhook. The second someone makes an appointment using the Acuity scheduler, everything is set in motion and your business remains on track.

Think You Have Acuity Stumped? Think Again

There isn’t a special case you can dream up that can’t be solved with Acuity, webhooks, coding know-how, and a little elbow grease. For example, say you teach courses or offer phone seminars to groups of clients, with a minimum number of sign-ups for the session to take place and a maximum number of clients allowed in any one session, and you allow clients to schedule these sessions at their convenience. Tracking the scheduling for this kind of offer would normally be a nightmare, but with Acuity’s webhooks it can all be automated.

You set up your Acuity calendar with the times you have available for these group lessons, and you wait for the first client to schedule a time. Your webhooks start a client count for the session, and send out emails to your current client list letting them know a new session has opened. As more clients sign up, the count increases and more emails go out. When you reach the minimum client count, you can trigger a confirmation email, send invoices and update your accounts payable, order any inventory you may need, even rent a conference room or other space assuming your venue has a web-based reservation form.

Set it up once, and your system is in place for all such sessions from here to eternity.

It’s webhooks forever and ever and ever…

Send text reminders at set intervals. Post on social media. Grant access to members-only parts of your site. You can even purchase and drop-ship product to clients when they make an appointment, if that’s something you want to do. Acuity has helped thousands of solopreneurs and businesses large and small tie their timekeeping and schedule-making tasks to other relevant areas of their business, no matter that those areas may be.

Are you ready to get your time management on track by letting your calendar keep track of itself and a whole lot more? Then sign up for a risk-free free trial today and see what Acuity can do for you. Once it gets its hooks into you—and into your business—you’ll be glad you took the bait.

Source:: scotch.io

Scotch.io v7: Why Another Redesign?

By Chris Sevilleja

Last week we released v7 of Scotch.io. This makes seven versions in 3.5 years! This was a big redesign for us since it is the foundation for where we want to go in the future.

The main points we wanted to drive home in this redesign were:

  • Faster! Almost as fast as the fastest site on Earth
  • Mobile speed. Google is very particular about mobile speed this year.
  • Combined platform. Completely on Laravel now!
  • Easier to find content. Search and real-time filtering (thanks Algolia)
  • Provide organization across all content (between guides/courses/tutorials)
  • Easier commenting and community growth (thanks Spot.IM)

Design!

A main feedback we got from visitors to the site is that they weren’t completely sure what Scotch.io was. Is it about the scotch alcohol? It looks like it’s some kind of programming. Is it a place to drink and code?

One thing we wanted to do a bit more of was branding. Small thing like adding the home page banner and a little blurb in the footer should go a long way.

Design is something we like to have fun with. Maybe it’s because we did the redesign in spring this year, but we wanted to go light and fun this time around.

Here’s our previous designs for reference:

v1 too ugly for a picture

v2

v3

v4

v5

v6: Added School and Pub

v7

Reading Experience

Our reading experience has always been an important point for us. Spending hours choosing a font is something that we do with every design. For v7, we landed on Nunito. Rounded and playful, easy to read, and works well with the light and fun design we had in mind.

We also put a bit more emphasis on the code blocks (there’s a drop shadow and subtle background on them to make them pop).

For reading experience, we are very particular about our typography sizing:

  • Line length: 80-100 characters
  • Line height: 1.65
  • Font size: 19-20px

Here’s a great writeup that mirrors our thoughts on the subject: https://uxplanet.org/typographical-dilemmas-the-experiment-on-positive-online-reading-experience-78e2f1897109

Speed, Speed, and More Speed

As with all other projects, new features get added, features get taken away. Old code is left to hang out in random places. Towards the end of v6, things felt a little sluggish to us.

We went back to the drawing board and came back with the following improvements:

Mobile Speed and SEO

With Google emphasizing mobile speed more and more this year (they are even using it to rank SEO), we wanted to make sure we were ready for the future. SEO is a big deal for us and we try to stay ahead of the curve in that regard.

I know it’s not the end-all-be-all metric, but Google’s PageSpeed metric is fun to try to rank high. We got our mobile rank up to a 99. Look at the reason we get docked that one point! Let’s just blame Google for this one! Great improvement considering our old site was ranking somewhere in the low 80s.

Things are a little lower on the desktop side with a score of 97. The reason for the docks here is for third-party JavaScript that we don’t load on mobile devices.

Check the Google mobile friendly test to see how your site ranks also:

AMP

Accelerated Mobile Pages (AMP) is something that we wanted to add in. It’s more geared towards news organizations in my opinion, but if Google thinks sites should have it, then we’re happy to add it in. AMP is essentially a very pared down version of your site that has to follow very strict guidelines. The result is a much faster mobile site that Google can push more prominently in search results.

See the differences between an AMP and normal mobile site.

Far Smaller Payload

The JavaScript for this version of the site clocks in at a tiny 22.2KB (minified and gzipped). Removed jQuery in this version and went vanilla JS. If you haven’t worked vanilla JS in a while, give it a shot. It’s a blast in ES6. Check out our Getting Started with JS for Web Dev course.

The CSS comes in at 30KB (minified and gzipped). It’s interesting that the CSS comes in at a heavier size than the JavaScript. Might need to strip away some things there.

We pulled in a few parts of Bootstrap (mostly the reset) and a flexbox framework we wanted to give a try called Bulma. After working with it a bit, I highly recommend giving it a try.

50KB for the both the CSS and JS was the goal we set and I’m happy we came very close to it!

Almost Everything is Cached

We heavily utilize memcached and redis to cache things. Helps bring our server’s application times down to an average 30ms. That really helps to bring our TTFB (time to first byte) down below 200ms.

Here’s some before and after stats thanks to SpeedCurve:

This is also a little skewed because the old site had full HTML caching and it’s being compared to the current site which doesn’t yet have fully HTML caching. We’re still figuring out the best possible option to add this in. We’ve been testing Varnish but configuring that with the site authentication and dynamic features has been a challenge.

Here’s some payload stats:

Overall, a solid step in the right direction! I could talk

HTTP/2 Improvements

HTTP/2 has some great features that we’ve been able to implement very easily.

Multiplexing:

The ability to pull down multiple file requests in one TCP connection. You don’t have to bundle and send your users one giant file anymore. Each page only sends the necessary files it needs!

Take our Courses and Tutorials pages for instance. The Algolia library needed to make all that real-time goodness work is 200kb. It doesn’t make sense to load all that on pages that won’t be using the files.

Server Push:

Normally you would send a document to the browser. The browser would parse it, find the and tags, and then go find the corresponding href and src. This is a time consuming process. With Server Push, you can set a header on the response and tell the browser what files to go get immediately. This adds up to be some awesome savings during the browser render.

On the Tutorials page, notice how the assets have the Push next to them in the Initiator column:

Now compare this to one of our other sites Scotch Box. Notice the lookup takes about 50ms instead of the immediate 5ms. Not the most significant timewise, but that’s a solid 90% savings!

Combined Platform

This is something we’ve wanted to do for a while now. We used to have the three branded sites:

  • Scotch.io: Main blog. Built on WordPress
  • Scotch Pub: Writing platform. Built on Laravel
  • Scotch School: Premium video courses. Built on Laravel

We are still fans of WordPress. If you use it correctly (don’t add every plugin under the sun) and understand how to build your own efficient themes, it can be a phenomenal tool. It helped us start this blog by just letting us write and not worrying about the underlying platform creation.

The main reason we went the Laravel route is to have more customization in the writing platform and the video courses.

It’s all Scotch now!

We ran into some confusion with the branding of the three parts. Now that we’ve got everything on Laravel, we’ve done away with the subdomains. Everything lives on the main scotch.io domain now. No more school.scotch.io or pub.scotch.io. This will be better in the long run and will help for SEO for those that are writing their posts with us since the main domain is more powerful than a subdomain.

Your posts will live at https://scotch.io/@coolio/my-post-slug!

Easier to Find Content

With 578 Tutorials, 28 Courses, and a lot more on the way, we wanted to make it easier to find content. Great content started getting buried and we’d like to help them rise to the surface again.

With the help of Algolia, we’ve gone real-time filtering on our archive pages. We’ll be adding more filters and improving this section greatly in the coming weeks.

Go ahead and give it a try. Let us know what if you like how it works!

Synced Up Content Between Guides/Courses/Tutorials

This goal hasn’t come to full fruition just yet. I believe we have the groundwork for it though. The goal has always been to be one of the absolute best learning resources on the web. Now that we have all this content, I feel there’s a disconnect between it all.

The flow of how things will go is:

  • Guides will be the top-level
  • Courses will be the premium guided content
  • Tutorials will be high quality written content

There is also a disconnect between finding related content from tutorials to courses to guides and that’s something that we’ll be working on.

Commenting and Community Growth

With this redesign we’ve switched away from Disqus and over to Spot.IM. All old comments have been ported over and this will be an experiment that we’d love to get feedback from you on.

The main reasons for the switch:

  • Disqus is slow. It loads a lotlotlot of scripts. This didn’t work well with our speed goal.
  • Spot.IM provides a great commenting platform
  • Spot.IM provides a great newsfeed feature to see discussions happening across the site (the plus icon on the right side of the site)
  • Spot.IM provides a great “popular in the community” widget

Here’s the newsfeed feature:

We’re really excited to use Spot.IM and see if we can get more engagement and great conversations going. See you in the comments!

The Future

The mission forward is the same mission we’ve always had:

Provide the highest quality tutorials for learning how to code.

We’ve been working at the tutorials and publishing a great deal this past year. Now that the Courses are in the mix, we introduced Guides to show a path of learning. We’ll be building the Guides out more so you can see that grow from here moving forward.

A Giant Thanks!

Thanks so much for all your support! Your feedback and participation in the Scotch community has turned this tiny side-project into something we never dreamed was possible!

Thanks to everyone that’s found our little blog and has stuck around whether in Slack, on Twitter and Facebook, and in the comments and forums.

It truly means the world that you, our amazing readers, have come from around the world to learn to code and fight the war on bugs with us!

Source:: scotch.io