JoabMendes / luizalabs_test

Luizalabs Django Coding Test

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Luizalabs Employee Manager (Django Coding Test)

Build Status

Index

Introduction

This test project was developed using Python3 + Django 2.0 as framework, with a REST API built with the DRF library support. Using SQLite for the dev environment and a dynamic database setting. The frontend uses Vue.js 2 with the Bootstrap Vue as its component library and it consumes the referenced Django API.

The project was created from a Python/Django + Vue.Js skeleton application that I use to bootstrap my projects.

Basic Requirements

Python dependencies

View requirements.txt file.

Javascript dependencies

View package.json file.

Build

After creating a python 3 environment and start working on it, you can run these following commands to to build, populate the database and serve the application in your machine:

# install javascript dependencies
$ npm install

# build javascript frontend instance
$ npm run build

# collect static files
$ ./manage.py collectstatic

# apply migrations
$ ./manage.py migrate

# populate the database for developement
$ ./seed_dev.sh

# run python server
$ ./manage.py runserver

The seed command will create a django admin superuser with the following credentials:

  • Login: admin
  • Password: admin123

Running on dev

There're existing shell scripts in the project root to execute the application serving in your local machine (run the building instructions first):

# Working on the project python environment, run the django server
$ ./run_django_dev.sh

# In another terminal windown run the npm hot reloader server
$ ./run_npm_dev.sh

# This command  will open the browser automatically on localhost:8080, but
# you should head to http://127.0.0.1:8000 instead.
# To check the django admin head to: http://127.0.0.1:8000/admin

Running tests

There's a shell script to run the style check and the django tests all together:

Working on the project python environment, run:
$ ./test.sh

If you want to run them separately:

# Style test with flake8
$ flake8 domain app api --exclude=*/migrations,*settings

# Django tests
$ ./manage.py test

Architecture

Domain view

The following diagram describes the designed domain for this challenge:

Domain Diagram

This domain is implemented in the domain.models.py module, which specifiess the Django ORM Classes. The employee domain is responsible to manage/store the Employee entities and despite the problem description showing the employee department as a field, I decided to normalize it in another table. Then, the Department domain will be responsible to manage/store the department entities of the application.

Package view

As I'm using Django for this project, by default it proposes the use of a layered MVC architecture (Models, Views and Controllers - In Django they are called Models, Templates and Views instead). The models provide an interface for the application domain data in an Object oriented structure. As I created an API, I don't use the Template layer and my Views implement the API logic and access to the endpoints: Here is a package diagram of this project:

Package Diagram

Other packages and modules are present in the source but the diagram represents the main ones that execute the API flow:

The app package contains the the django settings and the wsgi module. It also has the urls module, which provides the system urls and loads the main template.

The app.urls includes the api.urls instance loading the api package endpoints into the main application.

The api.urls module provides the REST API endpoints, loading the views from the api.views package.

The api.views package has the modules: api.department_view and the api.employee_view and they implement the REST api methods for the models, which are accessed from the domain.models module.

API Endpoints

The implemented API provides endpoints to manage the two existing domain entities (Department/Employee) in a REST format through the default HTTP methods and retuning json responses. The existing API endpoints are:

Employee endpoints:

Listing employees

Retrieves all existing employees instances in the database. It supports pagination if the pagination=1 param is specified.

  • endpoint: /api/v1/employee/
  • method: GET
  • params: These optional parameters can be specified in the endpoint URI
{
  "pagination": 1, 
  "offset:": 0,
  "employees_per_page": 10
}
  • 200 Response:
[
    {
        "department": {
            "id": 1,
            "name": "Architecture"
        },
        "email": "arnaldo@luizalabs.com",
        "id": 1,
        "name": "Arnaldo Pereira"
    },
    {
        "department": {
            "id": 2,
            "name": "E-commerce"
        },
        "email": "renato@luizalabs.com",
        "id": 2,
        "name": "Renato Pedigoni"
    },
    {
        "department": {
            "id": 3,
            "name": "Mobile"
        },
        "email": "catoto@luizalabs.com",
        "id": 3,
        "name": "Thiago Catoto"
    }
]

or empty:

[]

Retrieving an employee

Retrieves an employee by its id

  • endpoint: /api/v1/employee/<id>
  • method: GET
  • params: By URL
  • 200 Response:
{
    "department": {
        "id": 1,
        "name": "Architecture"
    },
    "email": "arnaldo@luizalabs.com",
    "id": 1,
    "name": "Arnaldo Pereira"
}
  • 400 Response
{"employee": ["Invalid pk \"10\" - object does not exist."]}

Creating an employee

Creates a new employee instance based on the specified payload

  • endpoint: /api/v1/employee/
  • method: POST
  • params:
{
   "name": "Arnaldo Pereira",
   "email": "arnaldo@luizalabs.com",
   "department": 1
}

As the department field is a fk, you need to specify the department id when creating an employee.

  • 201 Response:
{
    "department": {
        "id": 1,
        "name": "Architecture"
    },
    "email": "arnaldo@luizalabs.com",
    "id": 1,
    "name": "Arnaldo Pereira"
}
  • 400 Responses
{"department": ["Invalid pk \"10\" - object does not exist."]}
{"name": ["This field is required"]}
{"email": ["This field is required"]}
{"email": ["Enter a valid email address."]}
{"email": ["This field must be unique."]}

Updating an employee

Updates an employee instance based on the specified payload / id

  • endpoint: /api/v1/employee/<id>
  • method: PUT
  • params:
{
   "name": "Arnaldo Pereira Jr",
   "email": "arnaldo@luizalabs.com",
   "department": 2
}

As the department field is a fk, you need to specify the department id when updating an employee.

  • 200 Response:
{
    "department": {
        "id": 2,
        "name": "E-commerce"
    },
    "email": "arnaldo@luizalabs.com",
    "id": 1,
    "name": "Arnaldo Pereira Jr"
}
  • 400 Responses:
{"department": ["Invalid pk \"10\" - object does not exist."]}
{"name": ["This field is required"]}
{"email": ["This field is required"]}
{"email": ["Enter a valid email address."]}
{"email": ["This field must be unique."]}

Deleting an employee

Deletes an employee instance by the referenced id.

  • endpoint: /api/v1/employee/<id>
  • method: DELETE
  • params: None
  • 204 Response: No response body
  • 400 Responses:
{"employee": ["Invalid pk \"10\" - object does not exist."]}

Department endpoints:

Listing departments

Retrieves all existing department entities in the database.

  • endpoint: /api/v1/department/
  • method: GET
  • params: None
  • 200 Response:
[
    {
        "id": 1,
        "name": "Architecture"
    },
    {
        "id": 2,
        "name": "E-commerce"
    },
    {
        "id": 3,
        "name": "Mobile"
    }
]

Frontend

The frontend of this application is built using Vue.js as Single Page Application (SPA). The /index.html file is served at / as a Django TemplateView and it loads the Vue app configuration. It uses Webpack 4 to minimize the application and convert it to vanilla javascript in the static/build.js map.

The component library used is the Bootstrap Vue, which implements the Twitter Bootstrap directives as Vue components, speeding the frontend development. The Vue components of this project are hosted in the src package:

src/
├── App.vue             # The main template of the application which loads the same navbar throug the SPA
├── components          # This folder stores the children components
│   ├── Index.vue       # Index view - Lists/Delete the employees
│   ├── New.vue         # Add emplooye view - Form to create a new employe instance
│   └── Update.vue      # Update employee view - Form to update an employee instance
└── main.js             # The vue app configuration, where the SPA dependencies are loaded and the routes configured.

Considerations

Several improvements can be done this project and here are some of them:

  • A login page and only allow the access to the application by authenticated users.
  • Soft delete: currently when an instance is deleted it will be erased permanently from the database. Soft delete makes possible to recover the information from a inactive domain instance.
  • With soft delete enabled, defining the active scope of de domain entities is also important, so the retrieved information is always active.
  • Add pagination to the views with listings (Index.vue).
  • Add search and filters to the views with listings (Index.vue).

The extra features added to this project were:

  • The ability to update employees instances.
  • The normalization of the department field into a new table.
  • The API tests (api/tests/).
  • The code style test.

For questions email me at joabe.mdl@gmail.com.

About

Luizalabs Django Coding Test


Languages

Language:Vue 43.8%Language:Python 39.1%Language:JavaScript 13.9%Language:Shell 1.9%Language:HTML 1.3%