Manual Reference Source Test

pop-api

Build Status Windows Build Coverage Status Dependency Status devDependencies Status

Features

The pop-api project aims to provide the core modules for the popcorn-api project, but can also be used for other purposes by using middleware.

  • Cli middleware for reading user input with commander.js.
  • Database middleware for connection to MongoDB through mongoose.
  • Logging of routes and other information using winston.
  • Uses express under the hood with:
  • Interface for registering routes for express.
  • Data Access Layer (DAL) class for standard CRUD operations.
  • Route controller to handle routes for your content.

Installation

 $ npm install --save pop-api

Documentation

Usage

For your basic setup you have to create a controller which will handle the routes. Your controller needs to extend from the IController interface to implement the registerRoutes method which will be called during the setup.

The route controller below will be created with a constructor which takes an object as the parameter. This example will register a GET /hello route and sends a JSON object as a response with a greeting to the name provided by the object from the constructor.

// ./MyRouteController.js
import { IController } from 'pop-api'

// Extend your route controller from the 'IController' interface.
export default class MyRouteController extends IController {

  // The constructor takes an object as the parameter.
  constructor({name}) {
    super()

    this.name = name
  }

  // Implement the 'registerRoutes' method from the 'IController interface.
  registerRoutes(router, PopApi) {
    router.get('/hello', this.getHello.bind(this))
  }

  // Router middleware to execute on the 'GET /hello' route.
  getHello(req, res, next) {
    return res.json({
      message: `Hello, ${this.name}`
    })
  }

}

To initialize the API we create an array of the route controllers and their constructor arguments we want to register. Then we just call the init method with the route controllers array, and the name and version your API (needed for the Cli). The API should run by default on port 5000.

// ./index.js
import { PopApi } from 'pop-api'
import MyRouteController from './MyRouteController'
import { name, version } from './package.json'

;(async () => {
  try {
    // Define the controllers you want to use.
    const controllers = [{
      Controller: MyRouteController,  // The controller to register.
      args: {                         // The arguments passed down to the
        name: 'John'                  // The additional arguments to pass to
                                      // your route controller.
      }
    }]

    // Initiate your API with the necessary parameters.
    await PopApi.init({                
      controllers,  // The controllers to register.
      name,         // The name of your API.
      version       // The version of your API.
    })
    // API is available on port 5000.
    // GET http://localhost:5000/hello -> { message: 'Hello, John' }
  } catch (err) {
    console.log(err)
  }
})()

License

MIT License

Usage

For your basic setup you have to create a controller which will handle the routes. Your controller needs to extend from the IController interface to implement the registerRoutes method which will be called during the setup.

The route controller below will be created with a constructor which takes an object as the parameter. This example will register a GET /hello route and sends a JSON object as a response with a greeting to the name provided by the object from the constructor.

// ./MyRouteController.js
import { IController } from 'pop-api'

// Extend your route controller from the 'IController' interface.
export default class MyRouteController extends IController {

  // The constructor takes an object as the parameter.
  constructor({name}) {
    super()

    this.name = name
  }

  // Implement the 'registerRoutes' method from the 'IController interface.
  registerRoutes(router, PopApi) {
    router.get('/hello', this.getHello.bind(this))
  }

  // Router middleware to execute on the 'GET /hello' route.
  getHello(req, res, next) {
    return res.json({
      message: `Hello, ${this.name}`
    })
  }

}

To initialize the API we create an array of the route controllers and their constructor arguments we want to register. Then we just call the init method with the route controllers array, and the name and version your API (needed for the Cli). The API should run by default on port 5000.

// ./index.js
import { PopApi } from 'pop-api'
import MyRouteController from './MyRouteController'
import { name, version } from './package.json'

;(async () => {
  try {
    // Define the controllers you want to use.
    const controllers = [{
      Controller: MyRouteController,  // The controller to register.
      args: {                         // The arguments passed down to the
        name: 'John'                  // The additional arguments to pass to
                                      // your route controller.
      }
    }]

    // Initiate your API with the necessary parameters.
    await PopApi.init({                
      controllers,  // The controllers to register.
      name,         // The name of your API.
      version       // The version of your API.
    })
    // API is available on port 5000.
    // GET http://localhost:5000/hello -> { message: 'Hello, John' }
  } catch (err) {
    console.error(err)
  }
})()

Middleware

Cli

The Cli middleware uses commander.js modules to parse the input of the user. The middleware itself doesn't bind anything to the PopApi instance, instead it parses the input and run the API accordingly.

import { PopApi, Cli, Logger } from 'pop-api'
import { name, version } from './package.json'

const cliOpts = {
  name,               // The name of your application
  version,            // The version of your application
  argv: process.argv  // The arguments to parse
}
PopApi.use(Cli, cliOpts)

// Parsed the input given and binds options for the `Logger` middleware.
// See the documentation for the `Logger` middleware for more options.
const { pretty, quiet } = PopApi.loggerArgs
PopApi.use(Logger, {
  pretty,
  quiet
})

Database

The Database middleware bind the database key to the PopApi instance. This middleware allows you to connect() and disconnect() from MongoDB through mongoose, and you can export and import a collection with the exportCollection(collection, outputFile) and importCollection(collection, jsonFile) methods. The example below uses a .env file to store the optional username and password values to establish a connection with MongoDB.

# .env
# (Optional) Assuming you use the `dotenv` modules to get your username and
# password for the database connection
DATABASE_USERNAME=myUsername
DATABASE_PASSWORD=myPassword

Now setup the Database middleware:

// (Optional) Assuming you use the `dotenv` modules to get your username and
// password for the database connection
import 'dotenv/config'
import { PopApi, Database } from 'pop-api'
import MyModel from './MyModel'
import { name } from './package.json'

const databaseOpts = {
  database: name,                           // The name of the database.
  hosts: ['localhost'],                     // A lst of hosts to connect to.
  dbPort: 27017,                            // (Optional) The port of MongoDB.
  username: process.env.DATABASE_USERNAME,  // (Optional) The username to
                                            // connect to the hosts.
  password: process.env.DATABASE_PASSWORD   // (Optional) The password to
                                            // connect to the hosts.
}
PopApi.use(Database, databaseOpts)

// The database middleware can now be used to connect to the MongoDB database.
PopApi.database.connect()
  .then(() => {
    // Connection successful!
    return new MyModel({
      _id: 'John',
      name: 'John',
      slug: 'john'
    }).save()
  })
  .catch(err => {
    // Handle error
  })
  .then(() => {
    // Disconnect from MongoDB.
    PopApi.database.disconnect()
  })

HttpServer

The HttpServer middleware forks workers so heavy load process can run on different child processes. It also makes the express app listen on a port.

import { PopApi, HttpServer } from 'pop-api'

const httpServerOpts = {
  app: PopApi.app,   // The express instance from PopApi.
  serverPort: 5000,  // The port your API will be running on.
  workers: 2         // The amount of workers to fork.
}
PopApi.use(HttpServer, httpServerOpts)
// Doesn't bind anything to the PopApi instance, just forks the workers and
// makes the app listen on your configured port.

Logger

The Logger middleware uses the winston module to create a global logger object. This logger object has various levels to log, such as debug, info, warn and error. This middleware also binds an express middleware function to log the routes.

import { PopApi, Logger } from 'pop-api'
import { join } from 'path'
import { name } from './package.json'

const loggerOpts = {
  name,                                 // The name of the log file.
  logDir: join(...[__dirname, 'tmp']),  // The directory to store the logs in.
  pretty: true,                         // (Optional) Pretty output mode.
  quiet: false                          // (Optional) Quiet mode for no output.
}
PopApi.use(Logger, loggerOpts)

logger.info('\logger\' will be a global object')
// Other log levels you can use are:
//  - logger.debug()
//  - logger.info()
//  - logger.warn()
//  - logger.error()

// Log middleware for logging routes, used by the `Routes` middleware, or set
// it yourself.
const { httpLogger } = PopApi
PopApi.app.use(httpLogger)

Routes

The Routes middleware configures the express instance. It sets up the body-parser and compression middleware, as well as the error and security middleware. Thirdly it registers the controllers with their routes.

import { PopApi, Routes } from 'pop-api'
import MyRouteController from './MyRouteController'

const routesOpts = {
  app: PopApi.app,                  // The express instance from PopApi.
  controllers: [{                   // A list of controllers to register.
    Controller: MyRouteController,  // The controller you want to register.
    args: {}                        // The arguments to pass down to the
                                    // MyRouteController.
  }]
}
PopApi.use(Routes, routesOpts)
// Doesn't bind anything to the PopApi instance, just configures the middleware
// for express and registers the controllers.

Custom Middleware

The init method will register the default Cli, Database, Logger, Routes and Server middleware, but you can also extends the functionality of pop-api by using your own middleware. In the middleware example below we create a middleware class which will only hold a simple greeting.

// ./MyMiddleware.js
export default class MyMiddleware {

  // The first parameter will be the 'PopApi' instance, the second will be an
  // object you can use to configure your middleware.
  constructor(PopApi, {name}) {
    this.name = name

    PopApi.myMiddleware = this.myMiddleware()
  }

  myMiddleware() {
    return `Hello, ${this.name}`
  }

}

To use the middleware we created you call the use method. The first parameter will be the middleware class you want to create, the second parameter is optional, but can be used to configure your middleware.

// ./index.js
import { PopApi } from 'pop-api'
import MyMiddleware from './MyMiddleware'

// Use the middleware you created.
PopApi.use(MyMiddleware, {
  name: 'John'
})

// The middleware will be bound to the 'PopApi' instance.
const greeting = PopApi.myMiddleware
console.log(greeting) // Hello, John

Advanced Usage

The advanced setup has some more options to setup your API in regards with the database connection and the port your API will be listening on. But also how you can serve content through built-in route controllers as well as how to extend the built-in route controllers.

Mongoose Models

For the advanced setup we will create a model using mongoose and a route controller extending from BaseContentController. Below we create a simple mongoose model.

// ./myModel.js
import mongoose, { Schema } from 'mongoose'

// Create a simple mongoose schema.
const mySchema =  new Schema({
  _id: {
    type: String,
    required: true,
    index: {
      unique: true
    }
  },
  slug: {
    type: String,
    required: true
  },
  name: {
    type: String,
    required: true
  }
})

// Create a model from the schema.
export default mongoose.model('MyModel', mySchema)

ContentService

Before we create a route controller we need a ContentService object which ats as the DAL for the CRUD operations. The ContentService object will be used as a parameter when creating the route controller.

// ./myService.js
import { ContentService } from 'pop-api'
import MyModel from './myModel'

const myService = new ContentService({
  Model: MyModel,           // The model for the service.
  projection: { name: 1 },  // Projection used to display multiple items.
  query: {}                 // (Optional) The default query to fetch items.
})

// Methods available:
// - myService.getContents([base])
// - myService.getPage([sort, page, query])
// - myService.getContent(id, [projection])
// - myService.createContent(obj)
// - myService.createMany(arr)
// - myService.updateContent(id, obj)
// - myService.updateMany(arr)
// - myService.deleteContent(id)
// - myService.deleteMany(arr)
// - myService.getRandomContent()

export default myService

Now we create a route controller which extends from BaseContentController. This route controller can be used on it's own or classes can extend from it to implement new behaviour or override existing behaviour. The BaseContentController class already implements the registerRoutes method from IController and adds the following routes to your API (note the base path will be taken from the basePath value of your route controller):

  • GET /examples Get a list of a available pages to get.
  • GET /examples/:page Get a page of models.
  • GET /example/:id Get a single model.
  • POST /examples Create a new model.
  • PUT /example/:id Update an existing model.
  • DELETE /example/:id Delete a model.
  • GET /random/example Get a random model.

Controllers

The following example extends from BaseContentController, registers the default routes and implements a GET /hello route.

// ./MyRouteController.js
import { BaseContentController } from 'pop-api'

// Extend from the `BaseContentController` which has defaults methods for CRUD
// operations.
export default class MyRouteController extends BaseContentController {

  // The constructor of `BaseContentController` needs an instance of
  // `ContentService` which we will create later. It can also take additional
  // parameters for your own implementation.
  constructor({basePath, service, name}) {
    // binds: this.baseBath and this.service.
    super({basePath, service})

    this.name = name
  }

  // Implement the 'registerRoutes' method from the 'IController interface.
  registerRoutes(router, PopApi) {
    // Call the `registerRoutes` method from the `BaseContentController` class
    // to register the default routes.
    super.registerRoutes(router, PopApi)

    // And add additional routes for your route controller.
    router.get('/hello', this.getHello.bind(this))
  }

  // Router middleware to execute on the 'GET /hello' route.
  getHello(req, res, next) {
    return res.json({
      message: `Hello, ${this.name}`
    })
  }

}

Configuration

Now to initial your API we create a list of route controllers we want to register with their constructor parameters. This example also shows additional parameters to pass down to the init method of the PopApi instance.

// ./index.js
import express from 'express'
import { PopApi, ContentService } from 'pop-api'
import { join } from 'path'
import MyRouteController from './MyRouteController'
import myService from './myService'
import { name, version } from './package.json'

;(async () => {
  try {
    // Define the controllers you want to use.
    const controllers = [{
      Controller: MyRouteController,  // The controller to register.
      args: {                         // The arguments passed down to the
                                      // constructor of the controller.
        basePath: 'example',          // The base path to register the routes
                                      // to.
        service: myService,           // The content service for the
                                      // BaseContentController.
        name: 'John'                  // The additional arguments to pass to
      }                               // your route controller.
    }]

    // Initiate your API with optional parameters.
    await PopApi.init({
      app: express(),          // The express instance  to use.
      controllers,             // The controllers to register.
      name,                    // The name of your API.
      version,                 // The version of your API.
      logDir: join(...[        // (Optional) The directory to store the log
        __dirname,             // files in. Defaults to `./tmp`.
        '..',
        'tmp'
      ]),
      hosts: ['11.11.11.11'],  // (Optional) The hosts to connect to for
                               // MongoDB. Defaults to `['localhost']`.
      dbPort: 27019,           // (Optional) The port of MongoDB to connect to
                               // Defaults to `27017`.
      username: 'myUsername',  // (Optional) The username to connect to.
                               // MongoDB. Defaults to `null`.
      password: 'myPassword',  // (Optional) The password to connect to.
                               // MongoDB. Defaults to `null`.
      serverPort: 8080,        // (Optional) The port to run your API on.
                               // Defaults to `5000`.
      workers: 4               // The amount of workers to fork for the server.
                               // Defaults to `2`.
    }, [                       // (Optional) A list of middlewares to register.
      // Cli,                  // Defaults to:
      // Logger,               //  [Cli, Logger, Database, Routes, HtttpServer]
      // Database,
      // Routes,
      // HttpServer
    ])
    // API is available on port 8080.

    // GET http://localhost:8080/hello
    // { "message": "Hello, John" }

    // GET http://localhost:8080/examples
    // ["/examples/1', "/examples/2"]

    // GET http://localhost:8080/examples/1
    // [
    //   { "_id": "578df3efb618f5141202a196", "name": "John" },
    //   { "_id": "578df3efb618f5141202a196", "name": "Mary" }
    // ]

    // GET http://localhost:8080/example/578df3efb618f5141202a196
    // { "_id": "578df3efb618f5141202a196", "name": "John", "slug": "john" }

    // POST http://localhost:8080/examples
    // body: { "name": "Mary", "slug": "mary" }
    // { "_id": "578df3efb618f5141202a196", "name": "Mary", "slug": "mary" }

    // PUT http://localhost:8080/example/578df3efb618f5141202a196
    // body: { "name": "James", "slug": "james" }
    // { "_id": "578df3efb618f5141202a196", "name": "James", "slug": "james" }

    // DELETE http://localhost:8080/example/578df3efb618f5141202a196
    // { "_id": "578df3efb618f5141202a196", "name": "James", "slug:" :james" }

    // GET http://localhost:8080/random/example -> { }
    // { "_id": "578df3efb618f5141202a196", "name": "Mary", "slug": "mary" }
  } catch (err) {
    console.error(err)
  }
})()

Extending Middleware

The behaviour of the default middlewares can be overwritten or extended by creating a new class which extends from the base middleware class. This section will look into how you can do this for your own project.

Cli

The CLi middleware uses commander.js for parsing the command line input. The following example overrides the initOptions method to add additional options for your cli middleware. It also overrides the getHelp method to add an example which will be printed wiith the --help flag. Lastly it overrides the run method which parses the cli input and runs the prrogram based on the input.

// ./middlewares/MyCli.js
import { Cli } from 'pop-api'

export default class MyCli extends Cli {

  /**
   * @override
   */
  constructor(PopApi, {argv, name, version, myCliOption}) {
    // Do not pass down the 'argv' key so it does not get parsed by commander.
    super(PopApi, {name, version})

    // Bind our option to the instance.
    this.myCliOption = myCliOption

    // Run the Cli middleware.
    this.run(PopApi, argv)
  }

  /**
   * @override
   */
  initOptions() {
    // First initiate the options from the base Cli middleware.
    super.initOptions()

    // Now you can add your own options.
    return this.program
      .option('--my-option', 'My awesome option')
  }

  /**
   * @override
   */
  getHelp() {
    // Get the help message from the base Cli middleware.
    const baseHelp = super.getHelp()

    // And add your own message for your options.  
    return baseHelp.concat([
      `    $ ${this.name} --my-option`
    ])
  }

  // Method ot be executed when the `--my-option` flag is set.
  runMyOption() {
    console.log(`Executing my awesome option: ${this.myCliOption}`)
  }

  /**
   * @override
   */
  run(PopApi, argv) {
    // Now we parse the options.
    this.program.parse(argv)

    // Check the use input if your option flag has been filled.
    if (this.program.myOption) {
      return this.runMyOption()
    }

    // Run any other input options from the base Cli middleware.
    return super.run(PopApi)
  }

}

Database

By default the Database middleware uses mongoose to create a Connection to MongoDB. For this example we will create a MySQL connection with the mysql module. It overrides the connect and disconnect methods to establish and end a connection.

// ./middlewares/MySqlDatabase.js
import mysql from 'mysql'
import { Database } from 'pop-api'

export default class MySqlDatabase extends Database {

  /**
   * @override
   */
  constructor(PopApi: any, {
    database,
    hosts = ['localhost'],
    dbPort = 3306,
    username,
    password
  }) {
    super(PopApi, {
      database,
      hosts,
      dbPort,
      username,
      password
    })

    // Bind the connection to the instance to connect and disconnect.
    this.connection = mysql.createConnection({
      host: this.hosts[0],
      user: this.username,
      password: this.password,
      database: this.database,
      port: this.dbPort
    })
    // Set the database middleware as an instance of MySqlDatabase.
    PopApi.database = this
  }

  /**
   * @override
   */
  connect()  {
    return new Promise((resolve, reject) => {
      return this.connection
        .connect(err => err ? reject(err) : resolve('Connected'))
    })
  }

  /**
   * @override
   */
  disconnect() {
    return new Promise(resolve => this.connection.end())
  }

}

Logger

The Logger middleware uses winston by default as a logger. Here we will extend the default logger middleware to use pino. We override the createLoggerInstance to create an instance of pino and override the consoleFormatter to use as a formatter function for pino.

// ./middlewares/PinoLogger.js
import pino from 'pino'
import { join } from 'path'
import { Logger } from 'pop-api'
import { sprintf } from 'sprintf-js'

export default class PinoLogger extends Logger {

  /**
   * @override
   */
  consoleFormatter(args) {
    const level = pino.levels.labels[args.level]
    const color = this.getLevelColor(level)

    return sprintf(
      `\x1b[0m[%s] ${color}%5s:\x1b[0m %2s/%d: \x1b[36m%s\x1b[0m`,
      new Date(args.time).toISOString(),
      level.toUpperCase(),
      this.name,
      args.pid,
      args.msg
    )
  }

  /**
   * @override
   */
  createLoggerInstance(suffix, pretty) {
    // Let the http logger middleware be handled by the base Logger middleware.
    if (suffix === 'http') {
      return super.createLoggerInstance(suffix, pretty)
    }

    const prettyPino = pino.pretty({
      // Or don't use a formatter at all.
      formatter: this.consoleFormatter.bind(this)
    })
    prettyPino.pipe(process.stdout)

    // Create our logger object.
    return pino({
      name: `${this.name}-${suffix}`,
      safe: true
    }, prettyPino)
  }

}

Routes

The default web framework used by the Routes middleware is express. For this example we will extend the Routes middleware to use restfy as the web framework. For this we will override the preRoutes method to use middleware for restify instead of express.

// ./middlewares/RestifyRoutes.js
import helmet from 'helmet'
import restify from 'restify'
import { Routes } from 'pop-api'

export default class RestifyRoutes extends Routes {

  /**
   * @override
   */
  preRoutes(app) {
    // Register the middleware plugins for Restify.
    app.use(restify.plugins.bodyParser())
    app.use(restify.plugins.queryParser())
    app.use(restify.plugins.gzipResponse())

    // Use helmet middleware or any other for Restify.
    app.use(helmet())
    app.use(helmet.contentSecurityPolicy({
      directives: {
        defaultSrc: ['\'none\'']
      }
    }))
    app.use(this.removeServerHeader)
  }

}

Using Custom Middlewares

The 'init' method can take a list of middlewares as a second parameter. This list of middlewares will be used by the PopApi instance. All the middlewares will be initiated with options from the 'init' method, so you can add additional options to your middleware.

// ./index.js
import restify from 'restify'
import { PopApi, HttpServer, utils } from 'pop-api'

import {
  MyCli,
  MySqlDatabase,
  PinoLogger,
  RestifyRoutes
} from './middlewares'
import { name, version } from '../package.json'

(async () => {
  try {
    await PopApi.init({
      name,
      version,
      myCliOption,
      ...
    }, [
      MyCli, // Will be initiated with additional 'myCliOption' value.
      PinoLogger,
      MySqlDatabase,
      RestifyRoutes,
      HttpServer
    ])
  } catch (err) {
    console.error(err)
  }
})()

0.1.0 (2017-11-28)

Features

  • initial-release: Initial release for npm registry (c5269e7)

Contributing

So you're interested in giving us a hand? That's awesome! We've put together some brief guidelines that should help you get started quickly and easily.

There are lots and lots of ways to get involved, this document covers:

Raising Issues

If you're about to raise an issue because you think that you've found a problem with the application, or you'd like to make a request for a new feature in the codebase, or any other reason… please read this first.

The GitHub issue tracker is the preferred channel for bug reports, feature requests, and pull requests but respect the following restrictions:

  • Please do not use the issue tracker for personal support requests.
  • Please do not derail or troll issues. Keep the discussion on topic and respect the opinions of others.

Report A Bug

A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful - thank you!

Guidelines for bug reports:

  1. Use the GitHub issue search — check if the issue has already been reported.
  2. Check if the issue has been fixed — try to reproduce it using the latest master or look for closed issues.
  3. Include a screencast if relevant - Is your issue about a design or front end feature or bug? The most helpful thing in the world is if we can see what you're talking about. Just drop the picture after writing your issue, it'll be uploaded and shown to the developers.
  4. Use the Issue tab on GitHub to start creating a bug report. A good bug report shouldn't leave others needing to chase you up for more information. Be sure to include all the possible required details and the steps to take to reproduce the issue.

Feature Requests

Feature requests are welcome. Before you submit one be sure to:

  1. Use the GitHub Issues search and check the feature hasn't already been requested.
  2. Take a moment to think about whether your idea fits with the scope and aims of the project, or if it might better fit being an app/plugin.
  3. Remember, it's up to you to make a strong case to convince the project's leaders of the merits of this feature. Please provide as much detail and context as possible, this means explaining the use case and why it is likely to be common.
  4. Clearly indicate whether this is a feature request for the application itself, or for packages like Providers, Metadatas, or other.

Pull Requests

Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about raising an issue which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged. To make sure your PR is accepted as quickly as possible, you should be sure to have read all the guidelines on:

Commit Messages

This project uses the Conventional Commits convention. If you are not familiar with this convention please read about it first before creating a commit message or a PR.

Styleguides

JavaScript Styleguide

All JavaScript must adhere to JavaScript Standard Style.

  • Inline exports with expressions whenever possible

    // Use this:
    export default class ClassName {
    
    }
    
    // Instead of:
    class ClassName {
    
    }
    export default ClassName
    

Tests Styleguide

  • Include thoughtfully-worded, well-structured Mocha tests in the ./test folder.
  • Treat describe as a noun or situation.
  • Treat it as a statement about state or how an operation changes state.

Documentation Styleguide

  • Use Markdown.
  • Reference methods and classes in markdown with the custom {} notation:
    • Reference classes with {ClassName}
    • Reference instance methods with {ClassName.methodName}
    • Reference class methods with {ClassName#methodName}

Setting up for development

To setup your local machine to start working on the project you can follow these steps:

  1. Install MongoDB including mongoexport and mongoimport
  2. Install NodeJS (at least Node v7.10.1 or greater)
  3. Clone the repository with: git clone https://github.com/popcorn-official/pop-api.git
  4. Install dependencies npm i
  5. Install the flow-typed libraries with npm run flow-typed

npm scripts

The following npm-scripts are available in order to help you with the development of the project.

 $ npm run build    # Transform the code with 'babel'
 $ npm run docs     # Generate the documentation with 'esdoc'
 $ npm run debug    # Run the applicaiton in debug mode
 $ npm run dev      # Run the application in development mode
 $ npm run flow     # Check flow typings
 $ npm run lint     # Check javascript style
 $ npm run test     # Run unit tests

Git hooks

The following git hooks are available to ensure the changes you are about to make follow the styleguides and make sure your changes pass the tests.

pre-commit          # npm run lint && npm run flow
pre-push            # npm run test

Contributor Covenant Code of Conduct

Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language
  • Being respectful of differing viewpoints and experiences
  • Gracefully accepting constructive criticism
  • Focusing on what is best for the community
  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances
  • Trolling, insulting/derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or electronic address, without explicit permission
  • Other conduct which could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@popcorntime.sh. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4