☔ The DRY documentation build tool
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
- 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
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.
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.
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 withhazy.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]|"}
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
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
$ blot --help
(thorough documentation coming soon!)
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!'))
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.)
-
--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
andderef
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
andafterCompile
configuration files (root of project)