Skip to content

slurmulon/blot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

blot

☔ The DRY documentation build tool

tl;dr

API Blueprint + Transclusion + Queries = Ultra-DRY Docs

  • Build tool for normalized API Blueprints and fixtures
  • Unifies documentation and common fixtures via transclusion and queries
  • Eases the maintenance of documentation, fixtures and tests

Features

  • Dynamically link and build API Blueprints from normalized files (.md, .mson, .json, etc)
  • Reference, query and embed data using a JSON-friendly syntax
  • Generate random data anywhere using the same syntax
  • Extract and export JSON fixtures from API Blueprints
  • Export API Blueprints as HTML (web-component friendly, choose what you want by querying)
  • Multi-environment project configurations with build-specific overrides
  • Supports both standardized (bunyan) and pretty logs for sane debugging

Summary

blot minimizes duplication and introduces unification between documentation, fixtures, and API test suites. It sits on top of hazy and provides an abstract API Blueprint parser and generator.

API Blueprint is an open-source specification for programmatically documenting Restful APIs in pure Markdown. The specification is highly flexible and is focused on human readability. API Blueprints are also machine readable, so they naturally support tooling. They can be used to generate mock servers, automate integration testing, allow exportation of requests to tools such as Postman or cURL, and much much more.

A limitation of API blueprints is that they are static, and there are few (if any) tools for parsing documented requests and responses for programmatic (in-code) use in your integration and unit tests. My philosophy is that you should strive for a canonical source of fixtures in which all of your tests and documentation inherit from. Hercule, a library that blot integrates, promotes normalization by allowing data to be transcluded in markdown documents. blot also supports this through hazy, and either syntax may be used as they will both be processed. The reason that hazy is also used is because it provides additional interfaces for querying JSON fixtures and generating random data.

Fixtures

hazy is a node library for lazily processing dynamic fixture data. It provides a simple syntax for interpolating pattern-matched and/or random data into your fixtures. It alleviates the need for developers to constantly come up with names, addresses, etc. for their enormous amount of test data.

The most powerful feature of hazy is that it allows developers to dynamically embed fixtures (or sub-fixtures) via JsonPath patterns or by a simple string. This is very useful when creating and maintaining fixtures that share identical or related pieces of data, keeping your fixture data DRY as an application grows.

In blot, hazy acts as a standardized bridge between your documentation and tests. It pushes your fixtures out of your code and into a datastore such as your file system or a database, inherently canonical sources of data. Your API Blueprints and tests can then be dynamically generated by processing the fixtures via the blot API.

Examples

The following is an API blueprint decorated with some basic hazy tokens. The ~ keyword tells hazy to replace the token with categorized random data:

### Login a user [POST]

# POST /v1/auth
+ Request (application/json)

  { "username": "|~web.email|", "password": "|~text.word|" }

+ Response 200 (application/json)

  { "token": "|~misc.guid|", "refresh_token": "|~misc.guid|", "expires": "|~time.date|" }

# GET /v1/user/{id}
### Fetch a user [GET]

+ Response authentication (application/json)

  { "username": "|~web.email|", "first": "|~person.first|", "last": "|~person.last|", "address": "|~geo.address|" }

Alternatively, you can be even more lazy, which is encouraged for increased normalization. The following example shows how you can reference and embed fixtures that live on the filesystem using the @ operator.

# POST /v1/auth
### Login a user [POST]

+ Request (application/json)

  |@ auth-req.json|

+ Response 200 (application/json)

  |@ auth-user-res.json|

# GET /v1/user/{id}
### Fetch a user [GET]

+ Response 200 (application/json)

  |@ auth-user-res.json|

It can also be used alongside hercule's tranclusion operator :[]. One advantage is being able to reference URLs:

# POST /v1/auth
### Login a user [POST]

+ Attributes

  |@ auth-req.mson|

+ Request (application/json)

  :[](https://localhost:8000/data/reqs/auth-user-req.json)

+ Response 200 (application/json)

  :[](https://localhost:8000/data/reqs/auth-user-post-res.json)

# GET /v1/user/{id}
### Fetch a user [GET]

+ Response 200 (application/json)

  :[](https://localhost:8000/data/reqs/auth-user-get-res.json)

You may also freely leverage JsonPath in order to transclude fixtures by patterns with the $ operator:

Note

The $ operator will be prefixed to your pattern before being matched. Another way to look at is the text between the | bars will be interpreted as a literal JsonPath.

# POST /v1/auth
### Login a user [POST]

+ Request (application/json)

  |@ auth-req.json|

+ Response 200 (application/json)

  |$..user[0]|

# GET /v1/user/{id}
### Fetch a user [GET]

+ Response 200 (application/json)

  |$..user[0]|

Note

When using $, ensure that your fixtures have either been previously loaded using the @ operator, or by manually injecting your fixtures with hazy.fixture.register before parsing your API Blueprint(s)

Subsets of fixtures may also be targeted. The following GET user fixture is friends with four arbitrary users (selected from tail of list):

# POST /v1/auth
### Login a user [POST]

+ Request (application/json)

  |@ auth-req.json|

+ Response 200 (application/json)

  {"user": "|$..user[0]|", "friends": []}

# GET /v1/user/{id}
### Fetch a user [GET]

+ Response 200 (application/json)

  {"user": "|$..user[0]|", "friends": "|$..user.id[:2]|"}

Command Line

The easiest way to use blot is by running it as a command.

You can specify an API blueprint file to parse and export:

Standard-ized

$ blot compile -i docs.blot.apib --echo > docs.apib

Pretty

$ blot compile -i docs.blot.apib -o docs.apib --pretty

You may also pass in the raw data:

$ blot compile -d 'FORMAT: 1A
# The Simplest API

# GET /message
+ Response 200 (text/json)

{"message": "Hello, |~person.name|!", "id": "|~misc.guid|"}' -o docs.apib --pretty

Project

If you require a lot of flags, or your command just starts to become unwieldy and difficult to read, then a project configuration file can spare you from eye strain:

{
  "host": "https://example.blot.apps.madhax.io",
  "base": ".",
  "docs": {
    "src": "test/fixtures/apiblueprint/hazy.md",
    "dest": "dist/docs/test/fixtures/apiblueprint/hazy.apib",
    "export": true
  },
  "fixtures": {
    "src": "src/fixtures",
    "dest": "dist/fixtures",
    "export": false
  },
  "view": {
    "dest": "dist/api.blot.html",
    "export": true,
    "options": {
      "themeFullWidth": true,
      "themeVariables": "slate"
    },
    "elements": {
      "pluck": ["link", "style", "body > *"]
    },
    "attrs": {},
    "replace": [
      {
        "desc": "replaces positional anchor hrefs with Angular-friendly values",
        "match": "href=\"#([^'\"]+)['\"]", 
        "template": "ng-click=\"scrollTo('|=$sub[0]|')\""
      }
    ]
  },
  "logging": false,
  "pretty": false
}

To build your documentation with a project file, simply provide the path of the configuration as the first argument after your command:

$ blot [command] /path/to/blot.config.json

Note

When a project file is used, blot implicitly sets the configuration file's containing folder as it's current working directory.

An example project can be found in blot/exaxmple/render/blot.json and can be built with the following command (--pretty is of course optional):

$ cd /path/to/blot
$ blot render example/render/blot.json --pretty

Help?

$ blot --help

(thorough documentation coming soon!)

Node

The node module allows you to monkey-patch special functionality and data to your fixtures. You can then inject your monkey-patched hazy pool by setting blot.interpolator, which is used whenever API blueprints are processed.

The following example attaches a created property to all fixtures. It also appends a fixture query parameter to any fixture with a url property (deep):

#! /usr/bin/env node

import hazy from 'hazy'
import blot from 'blot'
import moment from 'moment'

// ensure all fixtures have a created date
hazy.matcher.config({
  path   : '$',
  handle : (fixture) => {
    return Object.assign({created: moment()}, fixture)
  }
})

// ensure any fixture urls are appended with a '&fixture' query param
hazy.matcher.config({
  path   : '$..url',
  handle : (url) => `${url}&fixture=true`
})

// globs and loads data from filesystem into hazy's fixture pool
hazy.fixture.glob('**/fixtures/*.json')

// load api blueprint, process fixtures against configured hazy pool, then export as a static API blueprint file
blot.apib
  .src('documentation.blot.apib')
  .then(apib   => blot.apib.dest(apib.compiled.markdown, 'dist/documentation.apib'))
  .then(result => blot.log().info('done exporting!'))

Install

Basic usage?

$ npm install blot

Contributing?

$ git clone [email protected]:slurmulon/blot.git
$ cd blot
$ npm link

For local installations, you can run a binary of blot via node /path/to/your/project/node_modules/.bin/blot.

Global installation is only recommended for developer convenience. Local installation should always be used in projects and modules to prevent a variety of problems (dependency on machine config, version differences, cache, etc.)

TODO

  • --env CLI flag
  • Static fixture export
  • Support blot.fixtures.js for automated fixture setup for use in API Blueprint (pre-build)
  • Incorporate json-where
  • Incorporate ajv and deref for denormalizing JSON Schemas (tv4 doesn't handle external $ref properly)
  • Listen for *.blot.* file changes
  • Current working directory flag
  • Inheritable project config (more DRY)
  • Block statements that don't inject whitespace
  • Support fury.js
  • Support beforeCompile and afterCompile configuration files (root of project)